Introduction

This vignette illustrates the use of INLA for spatial prediction using examples from Blangiardo and Cameletti (2015) and Illian, Sørbye, and Rue (2012). For prediction of continuous spatial processes, the Lindgren, Rue, and Lindström (2011) stochastic partial differential equations (SPDE) approach is used to approximate the process through an areal Gaussian Markov random field (GMRF) representation. Finally, Log-Gaussian Cox process models are fit using the pseudodata approach of Simpson et al. (2016).

GMRF Background

Blangiardo and Cameletti (2015) section 6.1.

SPDE Background

Geostatistics Example

Toy dataset from Blangiardo and Cameletti (2015).

# Plot the data.
plot(s2 ~ s1, col = rgb(SPDEtoy$y / max(SPDEtoy$y), 0, 0), data = SPDEtoy, pch = 19, asp = 1, main = 'Toy Data')

# Create a mesh for the SPDE method and then plot it.
toy_mesh <- inla.mesh.2d(as.matrix(SPDEtoy[,c('s1', 's2')]), max.edge = c(0.1, 0.2))
plot(toy_mesh, asp = 1)
points(SPDEtoy$s1, SPDEtoy$s2, col = rgb(SPDEtoy$y / max(SPDEtoy$y), 0, 0, 0.5), pch = 20)

# SPDE projector matrix for estimation.
A_est <- inla.spde.make.A(toy_mesh, as.matrix(SPDEtoy[,c('s1', 's2')]))

# Initialize exponential covariance structure for SPDE.
spde <- inla.spde2.matern(mesh = toy_mesh, alpha = 2)

# Set up stack for estimation.
stack_index <- inla.spde.make.index(name = 'spatial_field', n.spde = spde$n.spde)
stack_est <- inla.stack(data = list(y = SPDEtoy$y), A = list(A_est), effects = list(c(stack_index, list(intercept = 1))), tag = 'est')

# Create a grid for prediction.
toy_nx <- 50
toy_ny <- 50
toy_grid <- expand.grid(x = seq(0, 1, length.out = toy_nx), y = seq(0, 1, length.out = toy_ny))

# SPDE projector matrix for prediction.
A_pred <- inla.spde.make.A(mesh = toy_mesh, loc = as.matrix(toy_grid))

# Set up stacks for prediction.
stack_latent <- inla.stack(data = list(xi = NA), A = list(A_pred), effects = list(stack_index), tag = 'pred_latent')
stack_response <- inla.stack(data = list(y = NA), A = list(A_pred), effects = list(c(stack_index, list(intercept = 1))), tag = 'pred_response')

# Join all three stacks.
stacks <- inla.stack(stack_est, stack_latent, stack_response)

# Fit the model with INLA.
toy_fit <- inla(
  y ~ -1 + intercept + f(spatial_field, model = spde),
  data = inla.stack.data(stacks),
  control.predictor = list(A = inla.stack.A(stacks), compute = TRUE)
)

# Output posterior summaries.
toy_fit$summary.fixed
toy_fit$summary.hyperpar
# Extract posterior mean of latent spatial field.
index_latent <- inla.stack.index(stacks, tag = 'pred_latent')$data
post_mean <- toy_fit$summary.linear.predictor[index_latent, 'mean']
post_sd <- toy_fit$summary.linear.predictor[index_latent, 'sd']

# Plot the posterior mean and SD of the latent spatial field.
plot(im(matrix(post_mean, nrow = toy_nx, ncol = toy_ny), xrange = range(toy_grid$x), yrange = range(toy_grid$y)), main = 'Posterior Mean of Spatial Field')
points(SPDEtoy$s1, SPDEtoy$s2, col = rgb(SPDEtoy$y / max(SPDEtoy$y), 0, 0, 0.5), pch = 20)

plot(im(matrix(post_sd, nrow = toy_nx, ncol = toy_ny), xrange = range(toy_grid$x), yrange = range(toy_grid$y)), main = 'Posterior SD of Spatial Field')
points(SPDEtoy$s1, SPDEtoy$s2, col = rgb(SPDEtoy$y / max(SPDEtoy$y), 0, 0, 0.5), pch = 20)

Bei Dataset

Example from Møller and Waagepetersen (2007), Beilschmiedia pendula Lauraceae locations in a plot in Panama. bei dataset in spatstat (Baddeley and Turner 2005).

# Plot the full point pattern.
plot(bei, pch = '.', cols = 'black', main = 'Realized Point Pattern')

# Take a sample of quadrats and plot the observed point pattern.
set.seed(84323)
N_QUADS <- 10
QUAD_SIZE <- 50

w_edge <- Frame(bei)$xrange[1]
e_edge <- Frame(bei)$xrange[2]
s_edge <- Frame(bei)$yrange[1]
n_edge <- Frame(bei)$yrange[2]

botleft <- cbind(
  runif(N_QUADS, w_edge, e_edge - QUAD_SIZE),
  runif(N_QUADS, s_edge, n_edge - QUAD_SIZE)
)
bei_interior <- lapply(seq_len(nrow(botleft)), function(r){return(
    cbind(
      botleft[r, 1] + c(0, 0, QUAD_SIZE, QUAD_SIZE),
      botleft[r, 2] + c(0, QUAD_SIZE, QUAD_SIZE, 0)
    )
  )})
bei_win <- do.call(
  union.owin,
  apply(botleft, 1, function(x){return(
    owin(x[1] + c(0, QUAD_SIZE), x[2] + c(0, QUAD_SIZE))
  )})
)
bei_hole <- bei[complement.owin(bei_win, frame = Frame(bei))]
bei_samp <- bei[bei_win]
bei_window_full <- Window(bei)

plot(bei_hole, main = 'Observed Region with Holes', pch = '.', cols = 'black')

plot(bei_window_full, main = 'Observed Sample')
plot(bei_win, add = TRUE)
plot(bei_samp, pch = '.', cols = 'black', add = TRUE)

# Parameters to experiment with.
MAX_EDGE_LENGTH <- 25
MAX_EDGE_EXT <- 50
MARGIN <- 100

# Mesh covering the site.
bei_boundary <- inla.mesh.segment(loc = do.call(cbind, vertices.owin(Window(bei))))
bei_full_mesh <- inla.mesh.create(
  boundary = bei_boundary,
  refine = list(max.edge = MAX_EDGE_LENGTH)
)
plot(bei_full_mesh, asp = 1)
points(bei, pch = 19, cex = 0.25, col = 'red')

# Mesh including a margin outside the site.
margin_mesh <- inla.mesh.2d(
  loc = bei_full_mesh$loc[,1:2], # Include nodes from site.
  offset = MARGIN,
  max.edge = MAX_EDGE_EXT # Fill in the rest with a coarser triangulation.
)
margin_spde <- inla.spde2.matern(mesh = margin_mesh)
plot(margin_mesh, asp = 1)
points(bei, pch = 19, cex = 0.25, col = 'red')

# Meshs with coarser resolution in quadrats.
quad_hole <- do.call(
  inla.mesh.segment,
  lapply(seq_along(bei_interior), function(i){
    return(inla.mesh.segment(loc = bei_interior[[i]], grp = i - 1))
  })
)
bei_hole_mesh0 <- inla.mesh.create(
  boundary = list(bei_boundary, quad_hole),
  refine = list(max.edge = MAX_EDGE_LENGTH)
)
plot(bei_hole_mesh0, asp = 1)
points(bei_hole, pch = 19, cex = 0.25, col = 'red')

bei_hole_mesh <- inla.mesh.create(
  loc = bei_hole_mesh0$loc[,1:2], # Include nodes from mesh with holes.
  boundary = bei_boundary,
  refine = list(max.edge = MAX_EDGE_EXT) # Fill in the rest with a coarser triangulation.
)
bei_hole_spde <- inla.spde2.matern(mesh = bei_hole_mesh)
plot(bei_hole_mesh, asp = 1)
points(bei_hole, pch = 19, cex = 0.25, col = 'red')

# Meshs with finer resolution in quadrats.
quad_bnd <- do.call(
  inla.mesh.segment,
  lapply(seq_along(bei_interior), function(i){
    return(inla.mesh.segment(loc = apply(bei_interior[[i]], 2, rev), grp = i - 1))
  })
)
bei_samp_mesh0 <- inla.mesh.create(
  boundary = quad_bnd,
  refine = list(max.edge = MAX_EDGE_LENGTH)
)
plot(bei_samp_mesh0, asp = 1)
points(bei_samp, pch = 19, cex = 0.25, col = 'red')

bei_samp_mesh <- inla.mesh.create(
  loc = bei_samp_mesh0$loc[,1:2], # Include nodes from mesh in quads.
  boundary = bei_boundary,
  refine = list(max.edge = MAX_EDGE_EXT) # Fill in the rest with a coarser triangulation.
)
bei_samp_spde <- inla.spde2.matern(mesh = bei_samp_mesh)
plot(bei_samp_mesh, asp = 1)
points(bei_samp, pch = 19, cex = 0.25, col = 'red')

# Meshes with varying resolution in quadrats and a margin.
margin_hole <- inla.mesh.2d(
  loc = bei_hole_mesh$loc[,1:2], # Include nodes from mesh with holes.
  offset = MARGIN,
  max.edge = MAX_EDGE_EXT # Fill in the rest with a coarser triangulation.
)
margin_hole_spde <- inla.spde2.matern(mesh = margin_hole)
plot(margin_hole, asp = 1)
points(bei_hole, pch = 19, cex = 0.25, col = 'red')

margin_samp <- inla.mesh.2d(
  loc = bei_samp_mesh$loc[,1:2], # Include nodes from quads.
  offset = MARGIN,
  max.edge = MAX_EDGE_EXT # Fill in the rest with a coarser triangulation.
)
margin_samp_spde <- inla.spde2.matern(mesh = margin_samp)
plot(margin_samp, asp = 1)
points(bei_samp, pch = 19, cex = 0.25, col = 'red')

obs_full <- rep(0, margin_mesh$n)
obs_full[inla.over_sp_mesh(as(Window(bei), 'SpatialPolygons'), margin_mesh, 'vertex')] <- 1

obs_hole <- rep(0, margin_hole$n)
obs_hole[inla.over_sp_mesh(as(Window(bei_hole), 'SpatialPolygons'), margin_hole, 'vertex')] <- 1

obs_samp <- rep(0, margin_samp$n)
obs_samp[inla.over_sp_mesh(as(Window(bei_samp), 'SpatialPolygons'), margin_samp, 'vertex')] <- 1

Bei Dataset with gridding

NGRID_X <- 40
NGRID_Y <- 20

centers <- gridcenters(
  dilation(bei_window_full, max(NGRID_X, NGRID_Y)),
  NGRID_X, NGRID_Y)
dx <- sum(unique(centers$x)[1:2] * c(-1, 1)) / 2
dy <- sum(unique(centers$y)[1:2] * c(-1, 1)) / 2
bei_df <- data.frame(x = centers$x, y = centers$y,
                     count = NA_integer_, area = NA_real_)

message('gridding full: ', system.time(
for(r in seq_len(nrow(bei_df))){
  bei_df$count[r] <- sum(bei$x >= bei_df$x[r] - dx &
                         bei$x < bei_df$x[r] + dx &
                         bei$y >= bei_df$y[r] - dy &
                         bei$y < bei_df$y[r] + dy)
  bei_df$area[r] <- area(Window(bei)[owin(c(bei_df$x[r] - dx, bei_df$x[r] + dx), c(bei_df$y[r] - dy, bei_df$y[r] + dy))])
}
)['elapsed'], ' seconds')

par(mar = c(0.5, 0, 2, 2))
plot(im(t(matrix(bei_df$count, nrow = length(unique(bei_df$x)))), unique(bei_df$x), unique(bei_df$y), unitname = 'meters'), ncolcours = range(bei_df$count) %*% c(-1, 1) + 1, main = 'Binned Tree Counts')
plot(bei_window_full, border = 'white', add = TRUE)
points(bei, pch = '.', col = 'black')

# SPDE projector matrix for estimation.
full_A_est <- inla.spde.make.A(
  margin_mesh,
  as.matrix(bei_df[bei_df$area > 0, c('x', 'y')])
)

# Set up stack for estimation.
stack_index <- inla.spde.make.index(name = 'spatial_field', n.spde = margin_spde$n.spde)
stack_est <- inla.stack(data = list(count = bei_df$count[bei_df$area > 0], larea = log(bei_df$area[bei_df$area > 0])), A = list(full_A_est), effects = list(c(stack_index, list(intercept = 1))), tag = 'est')

# SPDE projector matrix for prediction.
full_A_pred <- inla.spde.make.A(mesh = margin_mesh, loc = as.matrix(bei_df[,c('x', 'y')]))

# Set up stacks for prediction.
stack_latent <- inla.stack(data = list(xi = NA), A = list(full_A_pred), effects = list(stack_index), tag = 'pred_latent')
stack_response <- inla.stack(data = list(count = NA), A = list(full_A_pred), effects = list(c(stack_index, list(intercept = 1))), tag = 'pred_response')

# Join all three stacks.
stacks <- inla.stack(stack_est, stack_latent, stack_response)

# Fit the model with INLA.
message('gridded full: ', system.time(
bei_full_fit <- inla(
  count ~ -1 + intercept + f(spatial_field, model = margin_spde),
  offset = larea, family = 'poisson',
  data = inla.stack.data(stacks),
  control.predictor = list(A = inla.stack.A(stacks), compute = TRUE),
  verbose = TRUE
)
)['elapsed'], ' seconds')

# Output posterior summaries.
bei_full_fit$summary.fixed
bei_full_fit$summary.hyperpar
# Extract posterior mean of latent spatial field.
index_pred <- inla.stack.index(stacks, tag = 'pred_latent')$data
post_mean <- bei_full_fit$summary.linear.predictor[index_pred, 'mean']
post_sd <- bei_full_fit$summary.linear.predictor[index_pred, 'sd']

# Plot the posterior mean and SD of the latent spatial field.
plot(im(t(matrix(post_mean, nrow = length(unique(centers$x)), ncol = length(unique(centers$y)))), unique(centers$x), unique(centers$y)), main = 'Posterior Mean of Spatial Field')
plot(bei_window_full, add = TRUE)
points(bei, pch = '.', col = 'black')

plot(im(t(matrix(post_sd, nrow = length(unique(centers$x)), ncol = length(unique(centers$y)))), unique(centers$x), unique(centers$y)), main = 'Posterior SD of Spatial Field')
plot(bei_window_full, add = TRUE)
points(bei, pch = '.', col = 'black')

beihole_df <- data.frame(x = centers$x, y = centers$y,
                         count = NA_integer_, area = NA_real_)

message('gridding holes: ', system.time(
for(r in seq_len(nrow(beihole_df))){
  beihole_df$count[r] <- sum(bei_hole$x >= beihole_df$x[r] - dx &
                             bei_hole$x < beihole_df$x[r] + dx &
                             bei_hole$y >= beihole_df$y[r] - dy &
                             bei_hole$y < beihole_df$y[r] + dy)
  beihole_df$area[r] <- area(Window(bei_hole)[owin(c(beihole_df$x[r] - dx, beihole_df$x[r] + dx), c(beihole_df$y[r] - dy, beihole_df$y[r] + dy))])
}
)['elapsed'], ' seconds')

par(mar = c(0.5, 0, 2, 2))
plot(im(t(matrix(beihole_df$count, nrow = length(unique(beihole_df$x)))), unique(beihole_df$x), unique(beihole_df$y), unitname = 'meters'), ncolcours = range(beihole_df$count) %*% c(-1, 1) + 1, main = 'Binned Tree Counts')
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = '#00000040')

# SPDE projector matrix for estimation.
hole_A_est <- inla.spde.make.A(
  margin_hole,
  as.matrix(bei_df[beihole_df$area > 0, c('x', 'y')])
)
beisamp_df <- data.frame(x = centers$x, y = centers$y,
                         count = NA_integer_, area = NA_real_)

message('gridding sample: ', system.time(
for(r in seq_len(nrow(beisamp_df))){
  beisamp_df$count[r] <- sum(bei_samp$x >= beisamp_df$x[r] - dx &
                             bei_samp$x < beisamp_df$x[r] + dx &
                             bei_samp$y >= beisamp_df$y[r] - dy &
                             bei_samp$y < beisamp_df$y[r] + dy)
  beisamp_df$area[r] <- area(Window(bei_samp)[owin(c(beisamp_df$x[r] - dx, beisamp_df$x[r] + dx), c(beisamp_df$y[r] - dy, beisamp_df$y[r] + dy))])
}
)['elapsed'], ' seconds')

par(mar = c(0.5, 0, 2, 2))
plot(im(t(matrix(beisamp_df$count, nrow = length(unique(beisamp_df$x)))), unique(beisamp_df$x), unique(beisamp_df$y), unitname = 'meters'), ncolcours = range(beisamp_df$count) %*% c(-1, 1) + 1, main = 'Binned Tree Counts')
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = '#00000040')

# SPDE projector matrix for estimation.
samp_A_est <- inla.spde.make.A(
  margin_samp,
  as.matrix(bei_df[beisamp_df$area > 0, c('x', 'y')])
)

Bei Dataset with Simpson et al. (2016) method

This method relies upon the Lindgren, Rue, and Lindström (2011) approximation of the latent Gaussian field as a linear combination of a finite number of basis functions represented as a GMRF on the nodes of a triangulation of the space. Simpson et al. (2016) use the triangulation for numerical integration of the intensity function and show that the LGCP likelihood factors into the joint distribution of independent Poisson random variables corresponding to the points of the point pattern and the nodes of the triangulation. The model fitting proceeds using INLA to fit a Poisson model to pseudodata.

The pseudodata are constructed as follows.

Then \(y_{i} \sim Poisson(\alpha_{i}\eta_{i})\) where \(\log(\eta_{i})\) is the SPDE representation of the GF at the location of the \(i\)th pseudodatum. See the paper for tedious notation regarding the definition of \(\eta_{i}\). Ultimately, the nodes become Poisson random variables with means equal to the intensity at that their respective locations, observed points become Poisson random variables with means of 1, and the likelihood is approximately proportional to

$ {i=1}^{p+n} {i}^{y_{i}} (-{i} {i}). $

(Is there a missing \(\alpha_{i}\)?)

NPIX_X <- 400
NPIX_Y <- 200

full_pts <- cbind(bei$x, bei$y)

# Contruct the SPDE A matrix for nodes and points.
full_nV <- margin_mesh$n
full_nData <- dim(full_pts)[1]
full_LocationMatrix <- inla.mesh.project(margin_mesh, full_pts)$A
full_IntegrationMatrix <- sparseMatrix(i = 1:full_nV, j = 1:full_nV, x = rep(1, full_nV))
full_ObservationMatrix <- rbind(full_IntegrationMatrix, full_LocationMatrix)

# Get the integration weights.
full_IntegrationWeights <- diag(inla.mesh.fem(margin_mesh)$c0)
full_E_point_process <- c(obs_full * full_IntegrationWeights, rep(0, full_nData))

# Create the psuedodata.
full_fake_data <- c(rep(0, full_nV), rep(1, full_nData))

# Fit model to full site.
full_formula <- y ~ -1 + intercept + f(idx, model = margin_spde) # No covariates.
full_data <- list(y = full_fake_data, idx = 1:full_nV, intercept = rep(1, full_nV))
message('pseudodata full: ', system.time(
result_full <- inla(
  formula = full_formula,
  data = full_data,
  family = 'poisson',
  control.predictor = list(A = full_ObservationMatrix),
  E = full_E_point_process,
  verbose = TRUE
)
)['elapsed'], ' seconds')
result_full$summary.fixed
result_full$summary.hyperpar
# Plot surface.
proj_margin_mesh <- inla.mesh.projector(margin_mesh, dims = c(NPIX_X, NPIX_Y))
plot(im(t(inla.mesh.project(proj_margin_mesh, result_full$summary.random$idx[,'mean'])),
        xrange = Frame(bei)$x + c(-MARGIN, MARGIN),
        yrange = Frame(bei)$y + c(-MARGIN, MARGIN),
        unitname = c('meter', 'meters')),
        main = 'Posterior Mean Log-Intensity')
plot(Window(bei), border = 'white', add = TRUE)
points(bei, pch = '.', col = 'white')

plot(im(t(inla.mesh.project(proj_margin_mesh, result_full$summary.random$idx[,'sd'])),
        xrange = Frame(bei)$x + c(-MARGIN, MARGIN),
        yrange = Frame(bei)$y + c(-MARGIN, MARGIN),
        unitname = c('meter', 'meters')),
        main = 'Posterior SD Log-Intensity')
plot(Window(bei), border = 'white', add = TRUE)
points(bei, pch = '.', col = 'white')

hole_pts <- cbind(bei_hole$x, bei_hole$y)

# Contruct the SPDE A matrix for nodes and points.
hole_nV <- margin_hole$n
hole_nData <- dim(hole_pts)[1]
hole_LocationMatrix <- inla.mesh.project(margin_hole, hole_pts)$A
hole_IntegrationMatrix <- sparseMatrix(i = 1:hole_nV, j = 1:hole_nV, x = rep(1, hole_nV))
hole_ObservationMatrix <- rbind(hole_IntegrationMatrix, hole_LocationMatrix)

# Get the integration weights.
hole_IntegrationWeights <- diag(inla.mesh.fem(margin_hole)$c0)
hole_E_point_process <- c(obs_hole * hole_IntegrationWeights, rep(0, hole_nData))

# Create the psuedodata.
hole_fake_data <- c(rep(0, hole_nV), rep(1, hole_nData))

# Fit model to site with holes.
hole_formula <- y ~ -1 + intercept + f(idx, model = margin_hole_spde) # No covariates.
hole_data <- list(y = hole_fake_data, idx = 1:hole_nV, intercept = rep(1, hole_nV))
message('pseudodata hole: ', system.time(
result_hole <- inla(
  formula = hole_formula,
  data = hole_data,
  family = 'poisson',
  control.predictor = list(A = hole_ObservationMatrix),
  E = hole_E_point_process,
  verbose = TRUE
)
)['elapsed'], ' seconds')
result_hole$summary.fixed
result_hole$summary.hyperpar
# Plot surface.
proj_margin_hole <- inla.mesh.projector(margin_hole, dims = c(NPIX_X, NPIX_Y))
plot(im(t(inla.mesh.project(proj_margin_hole, result_hole$summary.random$idx[,'mean'])),
        xrange = Frame(bei)$x + c(-MARGIN, MARGIN),
        yrange = Frame(bei)$y + c(-MARGIN, MARGIN),
        unitname = c('meter', 'meters')),
        main = 'Posterior Mean Log-Intensity')
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = 'white')

plot(im(t(inla.mesh.project(proj_margin_hole, result_hole$summary.random$idx[,'sd'])),
        xrange = Frame(bei)$x + c(-MARGIN, MARGIN),
        yrange = Frame(bei)$y + c(-MARGIN, MARGIN),
        unitname = c('meter', 'meters')),
        main = 'Posterior SD Log-Intensity')
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = 'white')

samp_pts <- cbind(bei_samp$x, bei_samp$y)

# Contruct the SPDE A matrix for nodes and points.
samp_nV <- margin_samp$n
samp_nData <- dim(samp_pts)[1]
samp_LocationMatrix <- inla.mesh.project(margin_samp, samp_pts)$A
samp_IntegrationMatrix <- sparseMatrix(i = 1:samp_nV, j = 1:samp_nV, x = rep(1, samp_nV))
samp_ObservationMatrix <- rbind(samp_IntegrationMatrix, samp_LocationMatrix)

# Get the integration weights.
samp_IntegrationWeights <- diag(inla.mesh.fem(margin_samp)$c0)
samp_E_point_process <- c(obs_samp * samp_IntegrationWeights, rep(0, samp_nData))

# Create the psuedodata.
samp_fake_data <- c(rep(0, samp_nV), rep(1, samp_nData))

# Fit model to quadrat-sampled site.
samp_formula <- y ~ -1 + intercept + f(idx, model = margin_samp_spde) # No covariates.
samp_data <- list(y = samp_fake_data, idx = 1:samp_nV, intercept = rep(1, samp_nV))
message('pseudodata sampled: ', system.time(
result_samp <- inla(
  formula = samp_formula,
  data = samp_data,
  family = 'poisson',
  control.predictor = list(A = samp_ObservationMatrix),
  E = samp_E_point_process,
  verbose = TRUE
)
)['elapsed'], ' seconds')
result_samp$summary.fixed
result_samp$summary.hyperpar
# Plot surface.
proj_margin_samp <- inla.mesh.projector(margin_samp, dims = c(NPIX_X, NPIX_Y))
plot(im(t(inla.mesh.project(proj_margin_samp, result_samp$summary.random$idx[,'mean'])),
        xrange = Frame(bei)$x + c(-MARGIN, MARGIN),
        yrange = Frame(bei)$y + c(-MARGIN, MARGIN),
        unitname = c('meter', 'meters')),
        main = 'Posterior Mean Log-Intensity')
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = 'white')

plot(im(t(inla.mesh.project(proj_margin_samp, result_samp$summary.random$idx[,'sd'])),
        xrange = Frame(bei)$x + c(-MARGIN, MARGIN),
        yrange = Frame(bei)$y + c(-MARGIN, MARGIN),
        unitname = c('meter', 'meters')),
        main = 'Posterior SD Log-Intensity')
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = 'white')

Bei Dataset and inlabru

bei_full_spdf <- as.SpatialPoints.ppp(bei)
cmp_full <- coordinates ~ mySmooth(map = coordinates, model = margin_spde) + Intercept
message('inlabru full: ', system.time(
bei_full_lgcp <- lgcp(cmp_full, bei_full_spdf, options = list(verbose = TRUE))
)['elapsed'], ' seconds')
bei_full_lgcp$summary.fixed
bei_full_lgcp$summary.hyperpar
# Plot posterior means and posterior sd.
lambda_full <- predict(bei_full_lgcp, pixels(bei_full_mesh), ~ exp(mySmooth + Intercept))
plot(lambda_full)
plot(Window(bei), border = 'white', add = TRUE)
points(bei, pch = '.', col = 'white')

plot(lambda_full['sd'])
plot(Window(bei), border = 'white', add = TRUE)
points(bei, pch = '.', col = 'white')

bei_hole_spdf <- as.SpatialPoints.ppp(bei_hole)
cmp_hole <- coordinates ~ mySmooth(map = coordinates, model = margin_hole_spde) + Intercept
message('inlabru with holes: ', system.time(
bei_hole_lgcp <- lgcp(cmp_hole, bei_hole_spdf, options = list(verbose = TRUE))
)['elapsed'], ' seconds')
bei_hole_lgcp$summary.fixed
bei_hole_lgcp$summary.hyperpar
# Plot posterior means and posterior sd.
lambda_hole <- predict(bei_hole_lgcp, pixels(bei_hole_mesh0), ~ exp(mySmooth + Intercept))
plot(lambda_hole)
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = 'white')

plot(lambda_hole['sd'])
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = 'white')

bei_samp_spdf <- as.SpatialPoints.ppp(bei_samp)
cmp_samp <- coordinates ~ mySmooth(map = coordinates, model = margin_samp_spde) + Intercept
message('inlabru quadrats: ', system.time(
bei_samp_lgcp <- lgcp(cmp_samp, bei_samp_spdf, options = list(verbose = TRUE))
)['elapsed'], ' seconds')
# Plot posterior means and posterior sd.
lambda_samp <- predict(bei_samp_lgcp, pixels(bei_samp_mesh0), ~ exp(mySmooth + Intercept))
plot(lambda_samp)
plot(Window(bei), border = 'white', add = TRUE)
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = 'white')

plot(lambda_samp['sd'])
plot(Window(bei), border = 'white', add = TRUE)
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = 'white')

References

Baddeley, Adrian, and Rolf Turner. 2005. “Spatstat: An R Package for Analyzing Spatial Point Patterns.” Journal of Statistical Software 12 (6): 1–42.

Blangiardo, Marta, and Michela Cameletti. 2015. Spatial and Spatio-Temporal Bayesian Models with R-INLA. Wiley.

Illian, Janine B, Sigrunn H Sørbye, and Håvard Rue. 2012. “A Toolbox for Fitting Complex Spatial Point Process Models Using Integrated Nested Laplace Approximation (Inla).” The Annals of Applied Statistics, 1499–1530.

Lindgren, Finn, Håvard Rue, and Johan Lindström. 2011. “An Explicit Link Between Gaussian Fields and Gaussian Markov Random Fields: The Stochastic Partial Differential Equation Approach.” Journal of the Royal Statistical Society: Series B (Statistical Methodology) 73 (4): 423–98.

Møller, J, and RP Waagepetersen. 2007. “Modern Spatial Point Process Modelling and Inference.” Scandinavian Journal of Statistics 34: 643–711.

Simpson, Daniel, Janine B Illian, Finn Lindgren, Sigrunn H Sørbye, and Havard Rue. 2016. “Going Off Grid: Computationally Efficient Inference for Log-Gaussian Cox Processes.” Biometrika 103 (1): 49–70.

LS0tCnRpdGxlOiAiU3BhdGlhbCBQcmVkaWN0aW9uIHdpdGggSU5MQSIKYXV0aG9yOiAiS2VubmV0aCBBLiBGbGFnZyIKYmlibGlvZ3JhcGh5OiAiLi4vcmVmZXJlbmNlcy5iaWIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZmlnX2hlaWdodDogNgogICAgZmlnX3dpZHRoOiAxMAogICAgZmlnX2Nyb3A6IEZBTFNFCiAgICBoZWlnaHQ6ICI5NjBweCIKICAgIHdpZHRoOiAiNzIwcHgiCiAgICBzZWxmX2NvbnRhaW5lZDogVFJVRQotLS0KCgpgYGB7ciBzZXR1cCwgY2FjaGUgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UsIGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFLCBkcGkgPSAxNTAsIGZpZy5hbGlnbiA9ICdjZW50ZXInKQpgYGAKCmBgYHtyIHBhY2thZ2VzLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkoc3BhdHN0YXQpCmxpYnJhcnkoSU5MQSkKbGlicmFyeShpbmxhYnJ1KQpsaWJyYXJ5KG1hcHRvb2xzKQpgYGAKCgojIEludHJvZHVjdGlvbgoKVGhpcyB2aWduZXR0ZSBpbGx1c3RyYXRlcyB0aGUgdXNlIG9mIElOTEEgZm9yIHNwYXRpYWwgcHJlZGljdGlvbiB1c2luZyBleGFtcGxlcwpmcm9tIEByaW5sYSBhbmQgQGlsbGlhbmV0YWwuIEZvciBwcmVkaWN0aW9uIG9mIGNvbnRpbnVvdXMgc3BhdGlhbCBwcm9jZXNzZXMsCnRoZSBAbGluZGdyZW5ldGFsIHN0b2NoYXN0aWMgcGFydGlhbCBkaWZmZXJlbnRpYWwgZXF1YXRpb25zIChTUERFKSBhcHByb2FjaCBpcwp1c2VkIHRvIGFwcHJveGltYXRlIHRoZSBwcm9jZXNzIHRocm91Z2ggYW4gYXJlYWwgR2F1c3NpYW4gTWFya292IHJhbmRvbSBmaWVsZAooR01SRikgcmVwcmVzZW50YXRpb24uIEZpbmFsbHksIExvZy1HYXVzc2lhbiBDb3ggcHJvY2VzcyBtb2RlbHMgYXJlIGZpdCB1c2luZwp0aGUgcHNldWRvZGF0YSBhcHByb2FjaCBvZiBAc2ltcHNvbmV0YWwuCgoKIyBHTVJGIEJhY2tncm91bmQKCkByaW5sYSBzZWN0aW9uIDYuMS4KCi0gT2JzZXJ2YXRpb25zIGFnZ3JlZ2F0ZWQgdG8gZGlzam9pbnQgYXJlYWwgcmVnaW9ucyBpbmRleGVkIGJ5ICRpJC4KLSBFYWNoIHJlZ2lvbiBoYXMgdW5pcXVlIHBhcmFtZXRlciAkXHRoZXRhX3tpfSQuCi0gJFxtYXRoY2Fse059KGkpJCBpcyB0aGUgc2V0IG9mIGluZGljZXMgb2YgbmVpZ2hib3JzIG9mIHJlZ2lvbiAkaSQgYW5kCiAgJFxtYXRoY2Fse059X3tpfSA9IHxcbWF0aGNhbHtOfShpKXwkIGlzIHRoZSBudW1iZXIgb2YgbmVpZ2hib3Igb2YgcmVnaW9uICRpJC4KLSBMb2NhbCBNYXJrb3YgcHJvcGVydHk6IGdpdmVuICRcYm9sZHN5bWJvbHtcdGhldGF9X3tcbWF0aGNhbHtOfShpKX0kLAogICRcdGhldGFfe2l9JCBpcyBpbmRlcGVuZGVudCBvZiBhbGwgb3RoZXIgJFx0aGV0YV97an0kLgotIFRoZW4gdGhlIHByZWNpc2lvbiBtYXRyaXggJFxtYXRoYmZ7UX0kIG9mICRcYm9sZHN5bWJvbHtcdGhldGF9JCBpcyBzcGFyc2UKICBiZWNhdXNlIG9ubHkgbmVpZ2hib3JzIGhhdmUgbm9uemVybyBjb3ByZWNpc2lvbnMuCgotIEJlc2FnLVlvcmstTW9sbGkmI3gwMGU4OyBtb2RlbDoKICAgIC0gRXhjaGFuZ2VhYmxlIHJhbmRvbSBlZmZlY3RzICR1X3tpfSQgd2l0aCBpbnRyaW5zaWMgY29uZGl0aW9uYWwKICAgICAgYXV0b3JlZ3Jlc3NpdmUgKGlDQVIpIHN0cnVjdHVyZS4KICAgIC0gJHVfe2l9fFxtYXRoYmZ7dX1fey1pfSBcc2ltIFxtYXRocm17Tn1cbGVmdChcbXVfe2l9ICsgXHN1bV97an0gYV97aWp9ICh1X3tqfSAtIFxtdV97an0pIC8gXG1hdGhjYWx7Tn1fe2l9LCBcc2lnbWFfe3V9XnsyfSAvIFxtYXRoY2Fse059X3tpfVxyaWdodCkkCiAgICAtIChXaGF0IGFyZSB0aGUgJGFfe2lqfT8kKS4KICAgIC0gaUNBUiBpcyBhbiBpbXByb3BlciBwcmlvciBiZWNhdXNlIGNvdmFyaWFuY2UgbWF0cml4IG5vdCBwb3NpdGl2ZSBkZWZpbml0ZQogICAgICBidXQgdGhpcyBpcyBvayBmb3IgcmFuZG9tIGVmZmVjdHMuCgoKIyBTUERFIEJhY2tncm91bmQKCi0gU3BhdGlhbCBwcm9jZXNzICRceGkoXG1hdGhiZntzfSkkLgoKLSBTb2x2ZSAkKFxrYXBwYV57Mn0gLSBcRGVsdGEpXntcYWxwaGEgLyAyfShcdGF1IFx4aShcbWF0aGJme3N9KSkgPSBcbWF0aGNhbHtXfShcbWF0aGJme3N9KSQuCiAgICAtICRca2FwcGEkIGlzIGEgcmFuZ2UgcGFyYW1ldGVyLgogICAgLSAkXERlbHRhJCBpcyB0aGUgTGFwbGFjaWFuLgogICAgLSAkXGFscGhhJCBpcyBhIHNtb290aG5lc3MgcGFyYW1ldGVyLgogICAgLSAkXHRhdSQgYSBwcmVjaXNpb24gcGFyYW1ldGVyLgogICAgLSBFeGFjdCBhbmQgc2F0aW9uYXJ5IHNvbHV0aW9uOiAkXHhpKFxtYXRoYmZ7c30pJCBpcyBhIEdhdXNzaWFuIGZpZWxkIHdpdGggTWF0JiN4MDBlODtybiBjb3ZhcmlhbmNlIGZ1bmN0aW9uLgotIEZpbml0ZSBlbGVtZW50IGFwcHJveGltYXRpb24uLi4KCgojIEdlb3N0YXRpc3RpY3MgRXhhbXBsZQoKVG95IGRhdGFzZXQgZnJvbSBAcmlubGEuCgpgYGB7ciBzcGRldG95LCBmaWcud2lkdGggPSA2LCBvdXQud2lkdGggPSAnNjAlJ30KIyBQbG90IHRoZSBkYXRhLgpwbG90KHMyIH4gczEsIGNvbCA9IHJnYihTUERFdG95JHkgLyBtYXgoU1BERXRveSR5KSwgMCwgMCksIGRhdGEgPSBTUERFdG95LCBwY2ggPSAxOSwgYXNwID0gMSwgbWFpbiA9ICdUb3kgRGF0YScpCmBgYAoKYGBge3Igc3BkZW1lc2gsIGZpZy53aWR0aCA9IDYsIG91dC53aWR0aCA9ICc2MCUnfQojIENyZWF0ZSBhIG1lc2ggZm9yIHRoZSBTUERFIG1ldGhvZCBhbmQgdGhlbiBwbG90IGl0Lgp0b3lfbWVzaCA8LSBpbmxhLm1lc2guMmQoYXMubWF0cml4KFNQREV0b3lbLGMoJ3MxJywgJ3MyJyldKSwgbWF4LmVkZ2UgPSBjKDAuMSwgMC4yKSkKcGxvdCh0b3lfbWVzaCwgYXNwID0gMSkKcG9pbnRzKFNQREV0b3kkczEsIFNQREV0b3kkczIsIGNvbCA9IHJnYihTUERFdG95JHkgLyBtYXgoU1BERXRveSR5KSwgMCwgMCwgMC41KSwgcGNoID0gMjApCmBgYAoKYGBge3Igc3BkZWZpdH0KIyBTUERFIHByb2plY3RvciBtYXRyaXggZm9yIGVzdGltYXRpb24uCkFfZXN0IDwtIGlubGEuc3BkZS5tYWtlLkEodG95X21lc2gsIGFzLm1hdHJpeChTUERFdG95WyxjKCdzMScsICdzMicpXSkpCgojIEluaXRpYWxpemUgZXhwb25lbnRpYWwgY292YXJpYW5jZSBzdHJ1Y3R1cmUgZm9yIFNQREUuCnNwZGUgPC0gaW5sYS5zcGRlMi5tYXRlcm4obWVzaCA9IHRveV9tZXNoLCBhbHBoYSA9IDIpCgojIFNldCB1cCBzdGFjayBmb3IgZXN0aW1hdGlvbi4Kc3RhY2tfaW5kZXggPC0gaW5sYS5zcGRlLm1ha2UuaW5kZXgobmFtZSA9ICdzcGF0aWFsX2ZpZWxkJywgbi5zcGRlID0gc3BkZSRuLnNwZGUpCnN0YWNrX2VzdCA8LSBpbmxhLnN0YWNrKGRhdGEgPSBsaXN0KHkgPSBTUERFdG95JHkpLCBBID0gbGlzdChBX2VzdCksIGVmZmVjdHMgPSBsaXN0KGMoc3RhY2tfaW5kZXgsIGxpc3QoaW50ZXJjZXB0ID0gMSkpKSwgdGFnID0gJ2VzdCcpCgojIENyZWF0ZSBhIGdyaWQgZm9yIHByZWRpY3Rpb24uCnRveV9ueCA8LSA1MAp0b3lfbnkgPC0gNTAKdG95X2dyaWQgPC0gZXhwYW5kLmdyaWQoeCA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gdG95X254KSwgeSA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gdG95X255KSkKCiMgU1BERSBwcm9qZWN0b3IgbWF0cml4IGZvciBwcmVkaWN0aW9uLgpBX3ByZWQgPC0gaW5sYS5zcGRlLm1ha2UuQShtZXNoID0gdG95X21lc2gsIGxvYyA9IGFzLm1hdHJpeCh0b3lfZ3JpZCkpCgojIFNldCB1cCBzdGFja3MgZm9yIHByZWRpY3Rpb24uCnN0YWNrX2xhdGVudCA8LSBpbmxhLnN0YWNrKGRhdGEgPSBsaXN0KHhpID0gTkEpLCBBID0gbGlzdChBX3ByZWQpLCBlZmZlY3RzID0gbGlzdChzdGFja19pbmRleCksIHRhZyA9ICdwcmVkX2xhdGVudCcpCnN0YWNrX3Jlc3BvbnNlIDwtIGlubGEuc3RhY2soZGF0YSA9IGxpc3QoeSA9IE5BKSwgQSA9IGxpc3QoQV9wcmVkKSwgZWZmZWN0cyA9IGxpc3QoYyhzdGFja19pbmRleCwgbGlzdChpbnRlcmNlcHQgPSAxKSkpLCB0YWcgPSAncHJlZF9yZXNwb25zZScpCgojIEpvaW4gYWxsIHRocmVlIHN0YWNrcy4Kc3RhY2tzIDwtIGlubGEuc3RhY2soc3RhY2tfZXN0LCBzdGFja19sYXRlbnQsIHN0YWNrX3Jlc3BvbnNlKQoKIyBGaXQgdGhlIG1vZGVsIHdpdGggSU5MQS4KdG95X2ZpdCA8LSBpbmxhKAogIHkgfiAtMSArIGludGVyY2VwdCArIGYoc3BhdGlhbF9maWVsZCwgbW9kZWwgPSBzcGRlKSwKICBkYXRhID0gaW5sYS5zdGFjay5kYXRhKHN0YWNrcyksCiAgY29udHJvbC5wcmVkaWN0b3IgPSBsaXN0KEEgPSBpbmxhLnN0YWNrLkEoc3RhY2tzKSwgY29tcHV0ZSA9IFRSVUUpCikKCiMgT3V0cHV0IHBvc3RlcmlvciBzdW1tYXJpZXMuCnRveV9maXQkc3VtbWFyeS5maXhlZAp0b3lfZml0JHN1bW1hcnkuaHlwZXJwYXIKCiMgRXh0cmFjdCBwb3N0ZXJpb3IgbWVhbiBvZiBsYXRlbnQgc3BhdGlhbCBmaWVsZC4KaW5kZXhfbGF0ZW50IDwtIGlubGEuc3RhY2suaW5kZXgoc3RhY2tzLCB0YWcgPSAncHJlZF9sYXRlbnQnKSRkYXRhCnBvc3RfbWVhbiA8LSB0b3lfZml0JHN1bW1hcnkubGluZWFyLnByZWRpY3RvcltpbmRleF9sYXRlbnQsICdtZWFuJ10KcG9zdF9zZCA8LSB0b3lfZml0JHN1bW1hcnkubGluZWFyLnByZWRpY3RvcltpbmRleF9sYXRlbnQsICdzZCddCgojIFBsb3QgdGhlIHBvc3RlcmlvciBtZWFuIGFuZCBTRCBvZiB0aGUgbGF0ZW50IHNwYXRpYWwgZmllbGQuCnBsb3QoaW0obWF0cml4KHBvc3RfbWVhbiwgbnJvdyA9IHRveV9ueCwgbmNvbCA9IHRveV9ueSksIHhyYW5nZSA9IHJhbmdlKHRveV9ncmlkJHgpLCB5cmFuZ2UgPSByYW5nZSh0b3lfZ3JpZCR5KSksIG1haW4gPSAnUG9zdGVyaW9yIE1lYW4gb2YgU3BhdGlhbCBGaWVsZCcpCnBvaW50cyhTUERFdG95JHMxLCBTUERFdG95JHMyLCBjb2wgPSByZ2IoU1BERXRveSR5IC8gbWF4KFNQREV0b3kkeSksIDAsIDAsIDAuNSksIHBjaCA9IDIwKQpwbG90KGltKG1hdHJpeChwb3N0X3NkLCBucm93ID0gdG95X254LCBuY29sID0gdG95X255KSwgeHJhbmdlID0gcmFuZ2UodG95X2dyaWQkeCksIHlyYW5nZSA9IHJhbmdlKHRveV9ncmlkJHkpKSwgbWFpbiA9ICdQb3N0ZXJpb3IgU0Qgb2YgU3BhdGlhbCBGaWVsZCcpCnBvaW50cyhTUERFdG95JHMxLCBTUERFdG95JHMyLCBjb2wgPSByZ2IoU1BERXRveSR5IC8gbWF4KFNQREV0b3kkeSksIDAsIDAsIDAuNSksIHBjaCA9IDIwKQpgYGAKCiMgQmVpIERhdGFzZXQKCkV4YW1wbGUgZnJvbSBAbW9lbGxlcndhYWdlcGV0ZXJzZW4sIF9CZWlsc2NobWllZGlhIHBlbmR1bGEgTGF1cmFjZWFlXyBsb2NhdGlvbnMKaW4gYSBwbG90IGluIFBhbmFtYS4gYGJlaWAgZGF0YXNldCBpbiBgc3BhdHN0YXRgIFtAc3BhdHN0YXRdLgoKYGBge3IgYmVpcHRzfQojIFBsb3QgdGhlIGZ1bGwgcG9pbnQgcGF0dGVybi4KcGxvdChiZWksIHBjaCA9ICcuJywgY29scyA9ICdibGFjaycsIG1haW4gPSAnUmVhbGl6ZWQgUG9pbnQgUGF0dGVybicpCmBgYAoKYGBge3IgYmVpaG9sZX0KIyBUYWtlIGEgc2FtcGxlIG9mIHF1YWRyYXRzIGFuZCBwbG90IHRoZSBvYnNlcnZlZCBwb2ludCBwYXR0ZXJuLgpzZXQuc2VlZCg4NDMyMykKTl9RVUFEUyA8LSAxMApRVUFEX1NJWkUgPC0gNTAKCndfZWRnZSA8LSBGcmFtZShiZWkpJHhyYW5nZVsxXQplX2VkZ2UgPC0gRnJhbWUoYmVpKSR4cmFuZ2VbMl0Kc19lZGdlIDwtIEZyYW1lKGJlaSkkeXJhbmdlWzFdCm5fZWRnZSA8LSBGcmFtZShiZWkpJHlyYW5nZVsyXQoKYm90bGVmdCA8LSBjYmluZCgKICBydW5pZihOX1FVQURTLCB3X2VkZ2UsIGVfZWRnZSAtIFFVQURfU0laRSksCiAgcnVuaWYoTl9RVUFEUywgc19lZGdlLCBuX2VkZ2UgLSBRVUFEX1NJWkUpCikKYmVpX2ludGVyaW9yIDwtIGxhcHBseShzZXFfbGVuKG5yb3coYm90bGVmdCkpLCBmdW5jdGlvbihyKXtyZXR1cm4oCiAgICBjYmluZCgKICAgICAgYm90bGVmdFtyLCAxXSArIGMoMCwgMCwgUVVBRF9TSVpFLCBRVUFEX1NJWkUpLAogICAgICBib3RsZWZ0W3IsIDJdICsgYygwLCBRVUFEX1NJWkUsIFFVQURfU0laRSwgMCkKICAgICkKICApfSkKYmVpX3dpbiA8LSBkby5jYWxsKAogIHVuaW9uLm93aW4sCiAgYXBwbHkoYm90bGVmdCwgMSwgZnVuY3Rpb24oeCl7cmV0dXJuKAogICAgb3dpbih4WzFdICsgYygwLCBRVUFEX1NJWkUpLCB4WzJdICsgYygwLCBRVUFEX1NJWkUpKQogICl9KQopCmJlaV9ob2xlIDwtIGJlaVtjb21wbGVtZW50Lm93aW4oYmVpX3dpbiwgZnJhbWUgPSBGcmFtZShiZWkpKV0KYmVpX3NhbXAgPC0gYmVpW2JlaV93aW5dCmJlaV93aW5kb3dfZnVsbCA8LSBXaW5kb3coYmVpKQoKcGxvdChiZWlfaG9sZSwgbWFpbiA9ICdPYnNlcnZlZCBSZWdpb24gd2l0aCBIb2xlcycsIHBjaCA9ICcuJywgY29scyA9ICdibGFjaycpCmBgYAoKYGBge3IgYmVpc2FtcH0KcGxvdChiZWlfd2luZG93X2Z1bGwsIG1haW4gPSAnT2JzZXJ2ZWQgU2FtcGxlJykKcGxvdChiZWlfd2luLCBhZGQgPSBUUlVFKQpwbG90KGJlaV9zYW1wLCBwY2ggPSAnLicsIGNvbHMgPSAnYmxhY2snLCBhZGQgPSBUUlVFKQpgYGAKCmBgYHtyIGJlaW1lc2h9CiMgUGFyYW1ldGVycyB0byBleHBlcmltZW50IHdpdGguCk1BWF9FREdFX0xFTkdUSCA8LSAyNQpNQVhfRURHRV9FWFQgPC0gNTAKTUFSR0lOIDwtIDEwMAoKIyBNZXNoIGNvdmVyaW5nIHRoZSBzaXRlLgpiZWlfYm91bmRhcnkgPC0gaW5sYS5tZXNoLnNlZ21lbnQobG9jID0gZG8uY2FsbChjYmluZCwgdmVydGljZXMub3dpbihXaW5kb3coYmVpKSkpKQpiZWlfZnVsbF9tZXNoIDwtIGlubGEubWVzaC5jcmVhdGUoCiAgYm91bmRhcnkgPSBiZWlfYm91bmRhcnksCiAgcmVmaW5lID0gbGlzdChtYXguZWRnZSA9IE1BWF9FREdFX0xFTkdUSCkKKQpwbG90KGJlaV9mdWxsX21lc2gsIGFzcCA9IDEpCnBvaW50cyhiZWksIHBjaCA9IDE5LCBjZXggPSAwLjI1LCBjb2wgPSAncmVkJykKCiMgTWVzaCBpbmNsdWRpbmcgYSBtYXJnaW4gb3V0c2lkZSB0aGUgc2l0ZS4KbWFyZ2luX21lc2ggPC0gaW5sYS5tZXNoLjJkKAogIGxvYyA9IGJlaV9mdWxsX21lc2gkbG9jWywxOjJdLCAjIEluY2x1ZGUgbm9kZXMgZnJvbSBzaXRlLgogIG9mZnNldCA9IE1BUkdJTiwKICBtYXguZWRnZSA9IE1BWF9FREdFX0VYVCAjIEZpbGwgaW4gdGhlIHJlc3Qgd2l0aCBhIGNvYXJzZXIgdHJpYW5ndWxhdGlvbi4KKQptYXJnaW5fc3BkZSA8LSBpbmxhLnNwZGUyLm1hdGVybihtZXNoID0gbWFyZ2luX21lc2gpCnBsb3QobWFyZ2luX21lc2gsIGFzcCA9IDEpCnBvaW50cyhiZWksIHBjaCA9IDE5LCBjZXggPSAwLjI1LCBjb2wgPSAncmVkJykKCgojIE1lc2hzIHdpdGggY29hcnNlciByZXNvbHV0aW9uIGluIHF1YWRyYXRzLgpxdWFkX2hvbGUgPC0gZG8uY2FsbCgKICBpbmxhLm1lc2guc2VnbWVudCwKICBsYXBwbHkoc2VxX2Fsb25nKGJlaV9pbnRlcmlvciksIGZ1bmN0aW9uKGkpewogICAgcmV0dXJuKGlubGEubWVzaC5zZWdtZW50KGxvYyA9IGJlaV9pbnRlcmlvcltbaV1dLCBncnAgPSBpIC0gMSkpCiAgfSkKKQpiZWlfaG9sZV9tZXNoMCA8LSBpbmxhLm1lc2guY3JlYXRlKAogIGJvdW5kYXJ5ID0gbGlzdChiZWlfYm91bmRhcnksIHF1YWRfaG9sZSksCiAgcmVmaW5lID0gbGlzdChtYXguZWRnZSA9IE1BWF9FREdFX0xFTkdUSCkKKQpwbG90KGJlaV9ob2xlX21lc2gwLCBhc3AgPSAxKQpwb2ludHMoYmVpX2hvbGUsIHBjaCA9IDE5LCBjZXggPSAwLjI1LCBjb2wgPSAncmVkJykKYmVpX2hvbGVfbWVzaCA8LSBpbmxhLm1lc2guY3JlYXRlKAogIGxvYyA9IGJlaV9ob2xlX21lc2gwJGxvY1ssMToyXSwgIyBJbmNsdWRlIG5vZGVzIGZyb20gbWVzaCB3aXRoIGhvbGVzLgogIGJvdW5kYXJ5ID0gYmVpX2JvdW5kYXJ5LAogIHJlZmluZSA9IGxpc3QobWF4LmVkZ2UgPSBNQVhfRURHRV9FWFQpICMgRmlsbCBpbiB0aGUgcmVzdCB3aXRoIGEgY29hcnNlciB0cmlhbmd1bGF0aW9uLgopCmJlaV9ob2xlX3NwZGUgPC0gaW5sYS5zcGRlMi5tYXRlcm4obWVzaCA9IGJlaV9ob2xlX21lc2gpCnBsb3QoYmVpX2hvbGVfbWVzaCwgYXNwID0gMSkKcG9pbnRzKGJlaV9ob2xlLCBwY2ggPSAxOSwgY2V4ID0gMC4yNSwgY29sID0gJ3JlZCcpCgojIE1lc2hzIHdpdGggZmluZXIgcmVzb2x1dGlvbiBpbiBxdWFkcmF0cy4KcXVhZF9ibmQgPC0gZG8uY2FsbCgKICBpbmxhLm1lc2guc2VnbWVudCwKICBsYXBwbHkoc2VxX2Fsb25nKGJlaV9pbnRlcmlvciksIGZ1bmN0aW9uKGkpewogICAgcmV0dXJuKGlubGEubWVzaC5zZWdtZW50KGxvYyA9IGFwcGx5KGJlaV9pbnRlcmlvcltbaV1dLCAyLCByZXYpLCBncnAgPSBpIC0gMSkpCiAgfSkKKQpiZWlfc2FtcF9tZXNoMCA8LSBpbmxhLm1lc2guY3JlYXRlKAogIGJvdW5kYXJ5ID0gcXVhZF9ibmQsCiAgcmVmaW5lID0gbGlzdChtYXguZWRnZSA9IE1BWF9FREdFX0xFTkdUSCkKKQpwbG90KGJlaV9zYW1wX21lc2gwLCBhc3AgPSAxKQpwb2ludHMoYmVpX3NhbXAsIHBjaCA9IDE5LCBjZXggPSAwLjI1LCBjb2wgPSAncmVkJykKYmVpX3NhbXBfbWVzaCA8LSBpbmxhLm1lc2guY3JlYXRlKAogIGxvYyA9IGJlaV9zYW1wX21lc2gwJGxvY1ssMToyXSwgIyBJbmNsdWRlIG5vZGVzIGZyb20gbWVzaCBpbiBxdWFkcy4KICBib3VuZGFyeSA9IGJlaV9ib3VuZGFyeSwKICByZWZpbmUgPSBsaXN0KG1heC5lZGdlID0gTUFYX0VER0VfRVhUKSAjIEZpbGwgaW4gdGhlIHJlc3Qgd2l0aCBhIGNvYXJzZXIgdHJpYW5ndWxhdGlvbi4KKQpiZWlfc2FtcF9zcGRlIDwtIGlubGEuc3BkZTIubWF0ZXJuKG1lc2ggPSBiZWlfc2FtcF9tZXNoKQpwbG90KGJlaV9zYW1wX21lc2gsIGFzcCA9IDEpCnBvaW50cyhiZWlfc2FtcCwgcGNoID0gMTksIGNleCA9IDAuMjUsIGNvbCA9ICdyZWQnKQoKIyBNZXNoZXMgd2l0aCB2YXJ5aW5nIHJlc29sdXRpb24gaW4gcXVhZHJhdHMgYW5kIGEgbWFyZ2luLgptYXJnaW5faG9sZSA8LSBpbmxhLm1lc2guMmQoCiAgbG9jID0gYmVpX2hvbGVfbWVzaCRsb2NbLDE6Ml0sICMgSW5jbHVkZSBub2RlcyBmcm9tIG1lc2ggd2l0aCBob2xlcy4KICBvZmZzZXQgPSBNQVJHSU4sCiAgbWF4LmVkZ2UgPSBNQVhfRURHRV9FWFQgIyBGaWxsIGluIHRoZSByZXN0IHdpdGggYSBjb2Fyc2VyIHRyaWFuZ3VsYXRpb24uCikKbWFyZ2luX2hvbGVfc3BkZSA8LSBpbmxhLnNwZGUyLm1hdGVybihtZXNoID0gbWFyZ2luX2hvbGUpCnBsb3QobWFyZ2luX2hvbGUsIGFzcCA9IDEpCnBvaW50cyhiZWlfaG9sZSwgcGNoID0gMTksIGNleCA9IDAuMjUsIGNvbCA9ICdyZWQnKQptYXJnaW5fc2FtcCA8LSBpbmxhLm1lc2guMmQoCiAgbG9jID0gYmVpX3NhbXBfbWVzaCRsb2NbLDE6Ml0sICMgSW5jbHVkZSBub2RlcyBmcm9tIHF1YWRzLgogIG9mZnNldCA9IE1BUkdJTiwKICBtYXguZWRnZSA9IE1BWF9FREdFX0VYVCAjIEZpbGwgaW4gdGhlIHJlc3Qgd2l0aCBhIGNvYXJzZXIgdHJpYW5ndWxhdGlvbi4KKQptYXJnaW5fc2FtcF9zcGRlIDwtIGlubGEuc3BkZTIubWF0ZXJuKG1lc2ggPSBtYXJnaW5fc2FtcCkKcGxvdChtYXJnaW5fc2FtcCwgYXNwID0gMSkKcG9pbnRzKGJlaV9zYW1wLCBwY2ggPSAxOSwgY2V4ID0gMC4yNSwgY29sID0gJ3JlZCcpCmBgYAoKYGBge3IgZWZmb3J0fQpvYnNfZnVsbCA8LSByZXAoMCwgbWFyZ2luX21lc2gkbikKb2JzX2Z1bGxbaW5sYS5vdmVyX3NwX21lc2goYXMoV2luZG93KGJlaSksICdTcGF0aWFsUG9seWdvbnMnKSwgbWFyZ2luX21lc2gsICd2ZXJ0ZXgnKV0gPC0gMQoKb2JzX2hvbGUgPC0gcmVwKDAsIG1hcmdpbl9ob2xlJG4pCm9ic19ob2xlW2lubGEub3Zlcl9zcF9tZXNoKGFzKFdpbmRvdyhiZWlfaG9sZSksICdTcGF0aWFsUG9seWdvbnMnKSwgbWFyZ2luX2hvbGUsICd2ZXJ0ZXgnKV0gPC0gMQoKb2JzX3NhbXAgPC0gcmVwKDAsIG1hcmdpbl9zYW1wJG4pCm9ic19zYW1wW2lubGEub3Zlcl9zcF9tZXNoKGFzKFdpbmRvdyhiZWlfc2FtcCksICdTcGF0aWFsUG9seWdvbnMnKSwgbWFyZ2luX3NhbXAsICd2ZXJ0ZXgnKV0gPC0gMQpgYGAKCgojIyBCZWkgRGF0YXNldCB3aXRoIGdyaWRkaW5nCgpgYGB7ciBiZWlpbmxhLCBjYWNoZSA9IFRSVUV9Ck5HUklEX1ggPC0gNDAKTkdSSURfWSA8LSAyMAoKY2VudGVycyA8LSBncmlkY2VudGVycygKICBkaWxhdGlvbihiZWlfd2luZG93X2Z1bGwsIG1heChOR1JJRF9YLCBOR1JJRF9ZKSksCiAgTkdSSURfWCwgTkdSSURfWSkKZHggPC0gc3VtKHVuaXF1ZShjZW50ZXJzJHgpWzE6Ml0gKiBjKC0xLCAxKSkgLyAyCmR5IDwtIHN1bSh1bmlxdWUoY2VudGVycyR5KVsxOjJdICogYygtMSwgMSkpIC8gMgpiZWlfZGYgPC0gZGF0YS5mcmFtZSh4ID0gY2VudGVycyR4LCB5ID0gY2VudGVycyR5LAogICAgICAgICAgICAgICAgICAgICBjb3VudCA9IE5BX2ludGVnZXJfLCBhcmVhID0gTkFfcmVhbF8pCgptZXNzYWdlKCdncmlkZGluZyBmdWxsOiAnLCBzeXN0ZW0udGltZSgKZm9yKHIgaW4gc2VxX2xlbihucm93KGJlaV9kZikpKXsKICBiZWlfZGYkY291bnRbcl0gPC0gc3VtKGJlaSR4ID49IGJlaV9kZiR4W3JdIC0gZHggJgogICAgICAgICAgICAgICAgICAgICAgICAgYmVpJHggPCBiZWlfZGYkeFtyXSArIGR4ICYKICAgICAgICAgICAgICAgICAgICAgICAgIGJlaSR5ID49IGJlaV9kZiR5W3JdIC0gZHkgJgogICAgICAgICAgICAgICAgICAgICAgICAgYmVpJHkgPCBiZWlfZGYkeVtyXSArIGR5KQogIGJlaV9kZiRhcmVhW3JdIDwtIGFyZWEoV2luZG93KGJlaSlbb3dpbihjKGJlaV9kZiR4W3JdIC0gZHgsIGJlaV9kZiR4W3JdICsgZHgpLCBjKGJlaV9kZiR5W3JdIC0gZHksIGJlaV9kZiR5W3JdICsgZHkpKV0pCn0KKVsnZWxhcHNlZCddLCAnIHNlY29uZHMnKQoKcGFyKG1hciA9IGMoMC41LCAwLCAyLCAyKSkKcGxvdChpbSh0KG1hdHJpeChiZWlfZGYkY291bnQsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGJlaV9kZiR4KSkpKSwgdW5pcXVlKGJlaV9kZiR4KSwgdW5pcXVlKGJlaV9kZiR5KSwgdW5pdG5hbWUgPSAnbWV0ZXJzJyksIG5jb2xjb3VycyA9IHJhbmdlKGJlaV9kZiRjb3VudCkgJSolIGMoLTEsIDEpICsgMSwgbWFpbiA9ICdCaW5uZWQgVHJlZSBDb3VudHMnKQpwbG90KGJlaV93aW5kb3dfZnVsbCwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaSwgcGNoID0gJy4nLCBjb2wgPSAnYmxhY2snKQoKIyBTUERFIHByb2plY3RvciBtYXRyaXggZm9yIGVzdGltYXRpb24uCmZ1bGxfQV9lc3QgPC0gaW5sYS5zcGRlLm1ha2UuQSgKICBtYXJnaW5fbWVzaCwKICBhcy5tYXRyaXgoYmVpX2RmW2JlaV9kZiRhcmVhID4gMCwgYygneCcsICd5JyldKQopCgojIFNldCB1cCBzdGFjayBmb3IgZXN0aW1hdGlvbi4Kc3RhY2tfaW5kZXggPC0gaW5sYS5zcGRlLm1ha2UuaW5kZXgobmFtZSA9ICdzcGF0aWFsX2ZpZWxkJywgbi5zcGRlID0gbWFyZ2luX3NwZGUkbi5zcGRlKQpzdGFja19lc3QgPC0gaW5sYS5zdGFjayhkYXRhID0gbGlzdChjb3VudCA9IGJlaV9kZiRjb3VudFtiZWlfZGYkYXJlYSA+IDBdLCBsYXJlYSA9IGxvZyhiZWlfZGYkYXJlYVtiZWlfZGYkYXJlYSA+IDBdKSksIEEgPSBsaXN0KGZ1bGxfQV9lc3QpLCBlZmZlY3RzID0gbGlzdChjKHN0YWNrX2luZGV4LCBsaXN0KGludGVyY2VwdCA9IDEpKSksIHRhZyA9ICdlc3QnKQoKIyBTUERFIHByb2plY3RvciBtYXRyaXggZm9yIHByZWRpY3Rpb24uCmZ1bGxfQV9wcmVkIDwtIGlubGEuc3BkZS5tYWtlLkEobWVzaCA9IG1hcmdpbl9tZXNoLCBsb2MgPSBhcy5tYXRyaXgoYmVpX2RmWyxjKCd4JywgJ3knKV0pKQoKIyBTZXQgdXAgc3RhY2tzIGZvciBwcmVkaWN0aW9uLgpzdGFja19sYXRlbnQgPC0gaW5sYS5zdGFjayhkYXRhID0gbGlzdCh4aSA9IE5BKSwgQSA9IGxpc3QoZnVsbF9BX3ByZWQpLCBlZmZlY3RzID0gbGlzdChzdGFja19pbmRleCksIHRhZyA9ICdwcmVkX2xhdGVudCcpCnN0YWNrX3Jlc3BvbnNlIDwtIGlubGEuc3RhY2soZGF0YSA9IGxpc3QoY291bnQgPSBOQSksIEEgPSBsaXN0KGZ1bGxfQV9wcmVkKSwgZWZmZWN0cyA9IGxpc3QoYyhzdGFja19pbmRleCwgbGlzdChpbnRlcmNlcHQgPSAxKSkpLCB0YWcgPSAncHJlZF9yZXNwb25zZScpCgojIEpvaW4gYWxsIHRocmVlIHN0YWNrcy4Kc3RhY2tzIDwtIGlubGEuc3RhY2soc3RhY2tfZXN0LCBzdGFja19sYXRlbnQsIHN0YWNrX3Jlc3BvbnNlKQoKIyBGaXQgdGhlIG1vZGVsIHdpdGggSU5MQS4KbWVzc2FnZSgnZ3JpZGRlZCBmdWxsOiAnLCBzeXN0ZW0udGltZSgKYmVpX2Z1bGxfZml0IDwtIGlubGEoCiAgY291bnQgfiAtMSArIGludGVyY2VwdCArIGYoc3BhdGlhbF9maWVsZCwgbW9kZWwgPSBtYXJnaW5fc3BkZSksCiAgb2Zmc2V0ID0gbGFyZWEsIGZhbWlseSA9ICdwb2lzc29uJywKICBkYXRhID0gaW5sYS5zdGFjay5kYXRhKHN0YWNrcyksCiAgY29udHJvbC5wcmVkaWN0b3IgPSBsaXN0KEEgPSBpbmxhLnN0YWNrLkEoc3RhY2tzKSwgY29tcHV0ZSA9IFRSVUUpLAogIHZlcmJvc2UgPSBUUlVFCikKKVsnZWxhcHNlZCddLCAnIHNlY29uZHMnKQoKIyBPdXRwdXQgcG9zdGVyaW9yIHN1bW1hcmllcy4KYmVpX2Z1bGxfZml0JHN1bW1hcnkuZml4ZWQKYmVpX2Z1bGxfZml0JHN1bW1hcnkuaHlwZXJwYXIKCiMgRXh0cmFjdCBwb3N0ZXJpb3IgbWVhbiBvZiBsYXRlbnQgc3BhdGlhbCBmaWVsZC4KaW5kZXhfcHJlZCA8LSBpbmxhLnN0YWNrLmluZGV4KHN0YWNrcywgdGFnID0gJ3ByZWRfbGF0ZW50JykkZGF0YQpwb3N0X21lYW4gPC0gYmVpX2Z1bGxfZml0JHN1bW1hcnkubGluZWFyLnByZWRpY3RvcltpbmRleF9wcmVkLCAnbWVhbiddCnBvc3Rfc2QgPC0gYmVpX2Z1bGxfZml0JHN1bW1hcnkubGluZWFyLnByZWRpY3RvcltpbmRleF9wcmVkLCAnc2QnXQoKIyBQbG90IHRoZSBwb3N0ZXJpb3IgbWVhbiBhbmQgU0Qgb2YgdGhlIGxhdGVudCBzcGF0aWFsIGZpZWxkLgpwbG90KGltKHQobWF0cml4KHBvc3RfbWVhbiwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoY2VudGVycyR4KSksIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNlbnRlcnMkeSkpKSksIHVuaXF1ZShjZW50ZXJzJHgpLCB1bmlxdWUoY2VudGVycyR5KSksIG1haW4gPSAnUG9zdGVyaW9yIE1lYW4gb2YgU3BhdGlhbCBGaWVsZCcpCnBsb3QoYmVpX3dpbmRvd19mdWxsLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpLCBwY2ggPSAnLicsIGNvbCA9ICdibGFjaycpCnBsb3QoaW0odChtYXRyaXgocG9zdF9zZCwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoY2VudGVycyR4KSksIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNlbnRlcnMkeSkpKSksIHVuaXF1ZShjZW50ZXJzJHgpLCB1bmlxdWUoY2VudGVycyR5KSksIG1haW4gPSAnUG9zdGVyaW9yIFNEIG9mIFNwYXRpYWwgRmllbGQnKQpwbG90KGJlaV93aW5kb3dfZnVsbCwgYWRkID0gVFJVRSkKcG9pbnRzKGJlaSwgcGNoID0gJy4nLCBjb2wgPSAnYmxhY2snKQpgYGAKCmBgYHtyIGJlaWhvbGVpbmxhfQpiZWlob2xlX2RmIDwtIGRhdGEuZnJhbWUoeCA9IGNlbnRlcnMkeCwgeSA9IGNlbnRlcnMkeSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50ID0gTkFfaW50ZWdlcl8sIGFyZWEgPSBOQV9yZWFsXykKCm1lc3NhZ2UoJ2dyaWRkaW5nIGhvbGVzOiAnLCBzeXN0ZW0udGltZSgKZm9yKHIgaW4gc2VxX2xlbihucm93KGJlaWhvbGVfZGYpKSl7CiAgYmVpaG9sZV9kZiRjb3VudFtyXSA8LSBzdW0oYmVpX2hvbGUkeCA+PSBiZWlob2xlX2RmJHhbcl0gLSBkeCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmVpX2hvbGUkeCA8IGJlaWhvbGVfZGYkeFtyXSArIGR4ICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZWlfaG9sZSR5ID49IGJlaWhvbGVfZGYkeVtyXSAtIGR5ICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZWlfaG9sZSR5IDwgYmVpaG9sZV9kZiR5W3JdICsgZHkpCiAgYmVpaG9sZV9kZiRhcmVhW3JdIDwtIGFyZWEoV2luZG93KGJlaV9ob2xlKVtvd2luKGMoYmVpaG9sZV9kZiR4W3JdIC0gZHgsIGJlaWhvbGVfZGYkeFtyXSArIGR4KSwgYyhiZWlob2xlX2RmJHlbcl0gLSBkeSwgYmVpaG9sZV9kZiR5W3JdICsgZHkpKV0pCn0KKVsnZWxhcHNlZCddLCAnIHNlY29uZHMnKQoKcGFyKG1hciA9IGMoMC41LCAwLCAyLCAyKSkKcGxvdChpbSh0KG1hdHJpeChiZWlob2xlX2RmJGNvdW50LCBucm93ID0gbGVuZ3RoKHVuaXF1ZShiZWlob2xlX2RmJHgpKSkpLCB1bmlxdWUoYmVpaG9sZV9kZiR4KSwgdW5pcXVlKGJlaWhvbGVfZGYkeSksIHVuaXRuYW1lID0gJ21ldGVycycpLCBuY29sY291cnMgPSByYW5nZShiZWlob2xlX2RmJGNvdW50KSAlKiUgYygtMSwgMSkgKyAxLCBtYWluID0gJ0Jpbm5lZCBUcmVlIENvdW50cycpCnBsb3QoV2luZG93KGJlaV9ob2xlKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaV9ob2xlLCBwY2ggPSAnLicsIGNvbCA9ICcjMDAwMDAwNDAnKQoKIyBTUERFIHByb2plY3RvciBtYXRyaXggZm9yIGVzdGltYXRpb24uCmhvbGVfQV9lc3QgPC0gaW5sYS5zcGRlLm1ha2UuQSgKICBtYXJnaW5faG9sZSwKICBhcy5tYXRyaXgoYmVpX2RmW2JlaWhvbGVfZGYkYXJlYSA+IDAsIGMoJ3gnLCAneScpXSkKKQpgYGAKCmBgYHtyIGJlaXNhbXBpbmxhfQpiZWlzYW1wX2RmIDwtIGRhdGEuZnJhbWUoeCA9IGNlbnRlcnMkeCwgeSA9IGNlbnRlcnMkeSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50ID0gTkFfaW50ZWdlcl8sIGFyZWEgPSBOQV9yZWFsXykKCm1lc3NhZ2UoJ2dyaWRkaW5nIHNhbXBsZTogJywgc3lzdGVtLnRpbWUoCmZvcihyIGluIHNlcV9sZW4obnJvdyhiZWlzYW1wX2RmKSkpewogIGJlaXNhbXBfZGYkY291bnRbcl0gPC0gc3VtKGJlaV9zYW1wJHggPj0gYmVpc2FtcF9kZiR4W3JdIC0gZHggJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJlaV9zYW1wJHggPCBiZWlzYW1wX2RmJHhbcl0gKyBkeCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmVpX3NhbXAkeSA+PSBiZWlzYW1wX2RmJHlbcl0gLSBkeSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmVpX3NhbXAkeSA8IGJlaXNhbXBfZGYkeVtyXSArIGR5KQogIGJlaXNhbXBfZGYkYXJlYVtyXSA8LSBhcmVhKFdpbmRvdyhiZWlfc2FtcClbb3dpbihjKGJlaXNhbXBfZGYkeFtyXSAtIGR4LCBiZWlzYW1wX2RmJHhbcl0gKyBkeCksIGMoYmVpc2FtcF9kZiR5W3JdIC0gZHksIGJlaXNhbXBfZGYkeVtyXSArIGR5KSldKQp9CilbJ2VsYXBzZWQnXSwgJyBzZWNvbmRzJykKCnBhcihtYXIgPSBjKDAuNSwgMCwgMiwgMikpCnBsb3QoaW0odChtYXRyaXgoYmVpc2FtcF9kZiRjb3VudCwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoYmVpc2FtcF9kZiR4KSkpKSwgdW5pcXVlKGJlaXNhbXBfZGYkeCksIHVuaXF1ZShiZWlzYW1wX2RmJHkpLCB1bml0bmFtZSA9ICdtZXRlcnMnKSwgbmNvbGNvdXJzID0gcmFuZ2UoYmVpc2FtcF9kZiRjb3VudCkgJSolIGMoLTEsIDEpICsgMSwgbWFpbiA9ICdCaW5uZWQgVHJlZSBDb3VudHMnKQpwbG90KFdpbmRvdyhiZWlfc2FtcCksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWlfc2FtcCwgcGNoID0gJy4nLCBjb2wgPSAnIzAwMDAwMDQwJykKCiMgU1BERSBwcm9qZWN0b3IgbWF0cml4IGZvciBlc3RpbWF0aW9uLgpzYW1wX0FfZXN0IDwtIGlubGEuc3BkZS5tYWtlLkEoCiAgbWFyZ2luX3NhbXAsCiAgYXMubWF0cml4KGJlaV9kZltiZWlzYW1wX2RmJGFyZWEgPiAwLCBjKCd4JywgJ3knKV0pCikKYGBgCgoKIyBCZWkgRGF0YXNldCB3aXRoIEBzaW1wc29uZXRhbCBtZXRob2QKClRoaXMgbWV0aG9kIHJlbGllcyB1cG9uIHRoZSBAbGluZGdyZW5ldGFsIGFwcHJveGltYXRpb24gb2YgdGhlIGxhdGVudCBHYXVzc2lhbgpmaWVsZCBhcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBhIGZpbml0ZSBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIHJlcHJlc2VudGVkCmFzIGEgR01SRiBvbiB0aGUgbm9kZXMgb2YgYSB0cmlhbmd1bGF0aW9uIG9mIHRoZSBzcGFjZS4gQHNpbXBzb25ldGFsIHVzZSB0aGUKdHJpYW5ndWxhdGlvbiBmb3IgbnVtZXJpY2FsIGludGVncmF0aW9uIG9mIHRoZSBpbnRlbnNpdHkgZnVuY3Rpb24gYW5kIHNob3cgdGhhdAp0aGUgTEdDUCBsaWtlbGlob29kIGZhY3RvcnMgaW50byB0aGUgam9pbnQgZGlzdHJpYnV0aW9uIG9mIGluZGVwZW5kZW50IFBvaXNzb24KcmFuZG9tIHZhcmlhYmxlcyBjb3JyZXNwb25kaW5nIHRvIHRoZSBwb2ludHMgb2YgdGhlIHBvaW50IHBhdHRlcm4gYW5kIHRoZSBub2RlcwpvZiB0aGUgdHJpYW5ndWxhdGlvbi4gVGhlIG1vZGVsIGZpdHRpbmcgcHJvY2VlZHMgdXNpbmcgSU5MQSB0byBmaXQgYSBQb2lzc29uCm1vZGVsIHRvIHBzZXVkb2RhdGEuCgpUaGUgcHNldWRvZGF0YSBhcmUgY29uc3RydWN0ZWQgYXMgZm9sbG93cy4KCi0gTGV0ICRuJCBiZSB0aGUgc2l6ZSBvZiB0aGUgcG9pbnQgcGF0dGVybi4KLSBMZXQgJHAkIGJlIHRoZSBudW1iZXIgb2Ygbm9kZXMgb2YgdHJpYW5ndWxhdGlvbi4KLSBEZWZpbmUgdGhlIHBzZXVkb2RhdGEgYXMKICAkXG1hdGhiZnt5fSA9ICh5X3sxfSwgXGRvdHMsIHlfe3B9LCB5X3twKzF9LCBcZG90cywgeV97cCtufSknJCB3aGVyZQogICR5X3tpfSA9IDAkIGZvciAkaSA9IDEsIFxkb3RzLCBwJCAodGhlIHRyYWluZ3VsYXRpb24gbm9kZXMpIGFuZCAkeV97aX0gPSAxJAogIGZvciAkaSA9IHArMSwgXGRvdHMsIHArbiQgKHRoZSBvYnNlcnZlZCBwb2ludHMpLgotIERlZmluZSAkXGJvbGRzeW1ib2x7XGFscGhhfSA9IChcYWxwaGFfe2l9LCBcZG90cywgXGFscGhhX3twfSwKICBcYWxwaGFfe3ArMX0sIFxkb3RzLCBcYWxwaGFfe3Arbn0pJyQgdG8gZW5jb2RlIHRoZSBudW1lcmljYWwgaW50ZWdyYXRpb24KICBzY2hlbWUgd2hlcmUgJFxhbHBoYV97aX0kIGlzIHRoZSBudW1lcmljYWwgaW50ZWdyYXRpb24gd2VpZ2h0IGZvcgogICRpID0gMSwgXGRvdHMsIHAkICh0aGUgdHJhaW5ndWxhdGlvbiBub2RlcykgYW5kICRcYWxwaGFfe2l9ID0gMCQgICBmb3IKICAkaSA9IHArMSwgXGRvdHMsIHArbiQgKHRoZSBvYnNlcnZlZCBwb2ludHMpLgoKVGhlbiAkeV97aX0gXHNpbSBQb2lzc29uKFxhbHBoYV97aX1cZXRhX3tpfSkkIHdoZXJlICRcbG9nKFxldGFfe2l9KSQgaXMgdGhlClNQREUgcmVwcmVzZW50YXRpb24gb2YgdGhlIEdGIGF0IHRoZSBsb2NhdGlvbiBvZiB0aGUgJGkkdGggcHNldWRvZGF0dW0uIFNlZQp0aGUgcGFwZXIgZm9yIHRlZGlvdXMgbm90YXRpb24gcmVnYXJkaW5nIHRoZSBkZWZpbml0aW9uIG9mICRcZXRhX3tpfSQuClVsdGltYXRlbHksIHRoZSBub2RlcyBiZWNvbWUgUG9pc3NvbiByYW5kb20gdmFyaWFibGVzIHdpdGggbWVhbnMgZXF1YWwgdG8gdGhlCmludGVuc2l0eSBhdCB0aGF0IHRoZWlyIHJlc3BlY3RpdmUgbG9jYXRpb25zLCBvYnNlcnZlZCBwb2ludHMgYmVjb21lIFBvaXNzb24KcmFuZG9tIHZhcmlhYmxlcyB3aXRoIG1lYW5zIG9mIDEsIGFuZCB0aGUgbGlrZWxpaG9vZCBpcyBhcHByb3hpbWF0ZWx5CnByb3BvcnRpb25hbCB0bwoKJApccHJvZF97aT0xfV57cCtufSBcZXRhX3tpfV57eV97aX19IFxleHAoLVxhbHBoYV97aX0gXGV0YV97aX0pLgokCgooSXMgdGhlcmUgYSBtaXNzaW5nICRcYWxwaGFfe2l9JD8pCgoKYGBge3IgYmVpbm9ncmlkLCBjYWNoZSA9IFRSVUV9Ck5QSVhfWCA8LSA0MDAKTlBJWF9ZIDwtIDIwMAoKZnVsbF9wdHMgPC0gY2JpbmQoYmVpJHgsIGJlaSR5KQoKIyBDb250cnVjdCB0aGUgU1BERSBBIG1hdHJpeCBmb3Igbm9kZXMgYW5kIHBvaW50cy4KZnVsbF9uViA8LSBtYXJnaW5fbWVzaCRuCmZ1bGxfbkRhdGEgPC0gZGltKGZ1bGxfcHRzKVsxXQpmdWxsX0xvY2F0aW9uTWF0cml4IDwtIGlubGEubWVzaC5wcm9qZWN0KG1hcmdpbl9tZXNoLCBmdWxsX3B0cykkQQpmdWxsX0ludGVncmF0aW9uTWF0cml4IDwtIHNwYXJzZU1hdHJpeChpID0gMTpmdWxsX25WLCBqID0gMTpmdWxsX25WLCB4ID0gcmVwKDEsIGZ1bGxfblYpKQpmdWxsX09ic2VydmF0aW9uTWF0cml4IDwtIHJiaW5kKGZ1bGxfSW50ZWdyYXRpb25NYXRyaXgsIGZ1bGxfTG9jYXRpb25NYXRyaXgpCgojIEdldCB0aGUgaW50ZWdyYXRpb24gd2VpZ2h0cy4KZnVsbF9JbnRlZ3JhdGlvbldlaWdodHMgPC0gZGlhZyhpbmxhLm1lc2guZmVtKG1hcmdpbl9tZXNoKSRjMCkKZnVsbF9FX3BvaW50X3Byb2Nlc3MgPC0gYyhvYnNfZnVsbCAqIGZ1bGxfSW50ZWdyYXRpb25XZWlnaHRzLCByZXAoMCwgZnVsbF9uRGF0YSkpCgojIENyZWF0ZSB0aGUgcHN1ZWRvZGF0YS4KZnVsbF9mYWtlX2RhdGEgPC0gYyhyZXAoMCwgZnVsbF9uViksIHJlcCgxLCBmdWxsX25EYXRhKSkKCiMgRml0IG1vZGVsIHRvIGZ1bGwgc2l0ZS4KZnVsbF9mb3JtdWxhIDwtIHkgfiAtMSArIGludGVyY2VwdCArIGYoaWR4LCBtb2RlbCA9IG1hcmdpbl9zcGRlKSAjIE5vIGNvdmFyaWF0ZXMuCmZ1bGxfZGF0YSA8LSBsaXN0KHkgPSBmdWxsX2Zha2VfZGF0YSwgaWR4ID0gMTpmdWxsX25WLCBpbnRlcmNlcHQgPSByZXAoMSwgZnVsbF9uVikpCm1lc3NhZ2UoJ3BzZXVkb2RhdGEgZnVsbDogJywgc3lzdGVtLnRpbWUoCnJlc3VsdF9mdWxsIDwtIGlubGEoCiAgZm9ybXVsYSA9IGZ1bGxfZm9ybXVsYSwKICBkYXRhID0gZnVsbF9kYXRhLAogIGZhbWlseSA9ICdwb2lzc29uJywKICBjb250cm9sLnByZWRpY3RvciA9IGxpc3QoQSA9IGZ1bGxfT2JzZXJ2YXRpb25NYXRyaXgpLAogIEUgPSBmdWxsX0VfcG9pbnRfcHJvY2VzcywKICB2ZXJib3NlID0gVFJVRQopCilbJ2VsYXBzZWQnXSwgJyBzZWNvbmRzJykKcmVzdWx0X2Z1bGwkc3VtbWFyeS5maXhlZApyZXN1bHRfZnVsbCRzdW1tYXJ5Lmh5cGVycGFyCgojIFBsb3Qgc3VyZmFjZS4KcHJval9tYXJnaW5fbWVzaCA8LSBpbmxhLm1lc2gucHJvamVjdG9yKG1hcmdpbl9tZXNoLCBkaW1zID0gYyhOUElYX1gsIE5QSVhfWSkpCnBsb3QoaW0odChpbmxhLm1lc2gucHJvamVjdChwcm9qX21hcmdpbl9tZXNoLCByZXN1bHRfZnVsbCRzdW1tYXJ5LnJhbmRvbSRpZHhbLCdtZWFuJ10pKSwKICAgICAgICB4cmFuZ2UgPSBGcmFtZShiZWkpJHggKyBjKC1NQVJHSU4sIE1BUkdJTiksCiAgICAgICAgeXJhbmdlID0gRnJhbWUoYmVpKSR5ICsgYygtTUFSR0lOLCBNQVJHSU4pLAogICAgICAgIHVuaXRuYW1lID0gYygnbWV0ZXInLCAnbWV0ZXJzJykpLAogICAgICAgIG1haW4gPSAnUG9zdGVyaW9yIE1lYW4gTG9nLUludGVuc2l0eScpCnBsb3QoV2luZG93KGJlaSksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWksIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKcGxvdChpbSh0KGlubGEubWVzaC5wcm9qZWN0KHByb2pfbWFyZ2luX21lc2gsIHJlc3VsdF9mdWxsJHN1bW1hcnkucmFuZG9tJGlkeFssJ3NkJ10pKSwKICAgICAgICB4cmFuZ2UgPSBGcmFtZShiZWkpJHggKyBjKC1NQVJHSU4sIE1BUkdJTiksCiAgICAgICAgeXJhbmdlID0gRnJhbWUoYmVpKSR5ICsgYygtTUFSR0lOLCBNQVJHSU4pLAogICAgICAgIHVuaXRuYW1lID0gYygnbWV0ZXInLCAnbWV0ZXJzJykpLAogICAgICAgIG1haW4gPSAnUG9zdGVyaW9yIFNEIExvZy1JbnRlbnNpdHknKQpwbG90KFdpbmRvdyhiZWkpLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpLCBwY2ggPSAnLicsIGNvbCA9ICd3aGl0ZScpCgoKaG9sZV9wdHMgPC0gY2JpbmQoYmVpX2hvbGUkeCwgYmVpX2hvbGUkeSkKCiMgQ29udHJ1Y3QgdGhlIFNQREUgQSBtYXRyaXggZm9yIG5vZGVzIGFuZCBwb2ludHMuCmhvbGVfblYgPC0gbWFyZ2luX2hvbGUkbgpob2xlX25EYXRhIDwtIGRpbShob2xlX3B0cylbMV0KaG9sZV9Mb2NhdGlvbk1hdHJpeCA8LSBpbmxhLm1lc2gucHJvamVjdChtYXJnaW5faG9sZSwgaG9sZV9wdHMpJEEKaG9sZV9JbnRlZ3JhdGlvbk1hdHJpeCA8LSBzcGFyc2VNYXRyaXgoaSA9IDE6aG9sZV9uViwgaiA9IDE6aG9sZV9uViwgeCA9IHJlcCgxLCBob2xlX25WKSkKaG9sZV9PYnNlcnZhdGlvbk1hdHJpeCA8LSByYmluZChob2xlX0ludGVncmF0aW9uTWF0cml4LCBob2xlX0xvY2F0aW9uTWF0cml4KQoKIyBHZXQgdGhlIGludGVncmF0aW9uIHdlaWdodHMuCmhvbGVfSW50ZWdyYXRpb25XZWlnaHRzIDwtIGRpYWcoaW5sYS5tZXNoLmZlbShtYXJnaW5faG9sZSkkYzApCmhvbGVfRV9wb2ludF9wcm9jZXNzIDwtIGMob2JzX2hvbGUgKiBob2xlX0ludGVncmF0aW9uV2VpZ2h0cywgcmVwKDAsIGhvbGVfbkRhdGEpKQoKIyBDcmVhdGUgdGhlIHBzdWVkb2RhdGEuCmhvbGVfZmFrZV9kYXRhIDwtIGMocmVwKDAsIGhvbGVfblYpLCByZXAoMSwgaG9sZV9uRGF0YSkpCgojIEZpdCBtb2RlbCB0byBzaXRlIHdpdGggaG9sZXMuCmhvbGVfZm9ybXVsYSA8LSB5IH4gLTEgKyBpbnRlcmNlcHQgKyBmKGlkeCwgbW9kZWwgPSBtYXJnaW5faG9sZV9zcGRlKSAjIE5vIGNvdmFyaWF0ZXMuCmhvbGVfZGF0YSA8LSBsaXN0KHkgPSBob2xlX2Zha2VfZGF0YSwgaWR4ID0gMTpob2xlX25WLCBpbnRlcmNlcHQgPSByZXAoMSwgaG9sZV9uVikpCm1lc3NhZ2UoJ3BzZXVkb2RhdGEgaG9sZTogJywgc3lzdGVtLnRpbWUoCnJlc3VsdF9ob2xlIDwtIGlubGEoCiAgZm9ybXVsYSA9IGhvbGVfZm9ybXVsYSwKICBkYXRhID0gaG9sZV9kYXRhLAogIGZhbWlseSA9ICdwb2lzc29uJywKICBjb250cm9sLnByZWRpY3RvciA9IGxpc3QoQSA9IGhvbGVfT2JzZXJ2YXRpb25NYXRyaXgpLAogIEUgPSBob2xlX0VfcG9pbnRfcHJvY2VzcywKICB2ZXJib3NlID0gVFJVRQopCilbJ2VsYXBzZWQnXSwgJyBzZWNvbmRzJykKcmVzdWx0X2hvbGUkc3VtbWFyeS5maXhlZApyZXN1bHRfaG9sZSRzdW1tYXJ5Lmh5cGVycGFyCgojIFBsb3Qgc3VyZmFjZS4KcHJval9tYXJnaW5faG9sZSA8LSBpbmxhLm1lc2gucHJvamVjdG9yKG1hcmdpbl9ob2xlLCBkaW1zID0gYyhOUElYX1gsIE5QSVhfWSkpCnBsb3QoaW0odChpbmxhLm1lc2gucHJvamVjdChwcm9qX21hcmdpbl9ob2xlLCByZXN1bHRfaG9sZSRzdW1tYXJ5LnJhbmRvbSRpZHhbLCdtZWFuJ10pKSwKICAgICAgICB4cmFuZ2UgPSBGcmFtZShiZWkpJHggKyBjKC1NQVJHSU4sIE1BUkdJTiksCiAgICAgICAgeXJhbmdlID0gRnJhbWUoYmVpKSR5ICsgYygtTUFSR0lOLCBNQVJHSU4pLAogICAgICAgIHVuaXRuYW1lID0gYygnbWV0ZXInLCAnbWV0ZXJzJykpLAogICAgICAgIG1haW4gPSAnUG9zdGVyaW9yIE1lYW4gTG9nLUludGVuc2l0eScpCnBsb3QoV2luZG93KGJlaV9ob2xlKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaV9ob2xlLCBwY2ggPSAnLicsIGNvbCA9ICd3aGl0ZScpCnBsb3QoaW0odChpbmxhLm1lc2gucHJvamVjdChwcm9qX21hcmdpbl9ob2xlLCByZXN1bHRfaG9sZSRzdW1tYXJ5LnJhbmRvbSRpZHhbLCdzZCddKSksCiAgICAgICAgeHJhbmdlID0gRnJhbWUoYmVpKSR4ICsgYygtTUFSR0lOLCBNQVJHSU4pLAogICAgICAgIHlyYW5nZSA9IEZyYW1lKGJlaSkkeSArIGMoLU1BUkdJTiwgTUFSR0lOKSwKICAgICAgICB1bml0bmFtZSA9IGMoJ21ldGVyJywgJ21ldGVycycpKSwKICAgICAgICBtYWluID0gJ1Bvc3RlcmlvciBTRCBMb2ctSW50ZW5zaXR5JykKcGxvdChXaW5kb3coYmVpX2hvbGUpLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpX2hvbGUsIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKCgpzYW1wX3B0cyA8LSBjYmluZChiZWlfc2FtcCR4LCBiZWlfc2FtcCR5KQoKIyBDb250cnVjdCB0aGUgU1BERSBBIG1hdHJpeCBmb3Igbm9kZXMgYW5kIHBvaW50cy4Kc2FtcF9uViA8LSBtYXJnaW5fc2FtcCRuCnNhbXBfbkRhdGEgPC0gZGltKHNhbXBfcHRzKVsxXQpzYW1wX0xvY2F0aW9uTWF0cml4IDwtIGlubGEubWVzaC5wcm9qZWN0KG1hcmdpbl9zYW1wLCBzYW1wX3B0cykkQQpzYW1wX0ludGVncmF0aW9uTWF0cml4IDwtIHNwYXJzZU1hdHJpeChpID0gMTpzYW1wX25WLCBqID0gMTpzYW1wX25WLCB4ID0gcmVwKDEsIHNhbXBfblYpKQpzYW1wX09ic2VydmF0aW9uTWF0cml4IDwtIHJiaW5kKHNhbXBfSW50ZWdyYXRpb25NYXRyaXgsIHNhbXBfTG9jYXRpb25NYXRyaXgpCgojIEdldCB0aGUgaW50ZWdyYXRpb24gd2VpZ2h0cy4Kc2FtcF9JbnRlZ3JhdGlvbldlaWdodHMgPC0gZGlhZyhpbmxhLm1lc2guZmVtKG1hcmdpbl9zYW1wKSRjMCkKc2FtcF9FX3BvaW50X3Byb2Nlc3MgPC0gYyhvYnNfc2FtcCAqIHNhbXBfSW50ZWdyYXRpb25XZWlnaHRzLCByZXAoMCwgc2FtcF9uRGF0YSkpCgojIENyZWF0ZSB0aGUgcHN1ZWRvZGF0YS4Kc2FtcF9mYWtlX2RhdGEgPC0gYyhyZXAoMCwgc2FtcF9uViksIHJlcCgxLCBzYW1wX25EYXRhKSkKCiMgRml0IG1vZGVsIHRvIHF1YWRyYXQtc2FtcGxlZCBzaXRlLgpzYW1wX2Zvcm11bGEgPC0geSB+IC0xICsgaW50ZXJjZXB0ICsgZihpZHgsIG1vZGVsID0gbWFyZ2luX3NhbXBfc3BkZSkgIyBObyBjb3ZhcmlhdGVzLgpzYW1wX2RhdGEgPC0gbGlzdCh5ID0gc2FtcF9mYWtlX2RhdGEsIGlkeCA9IDE6c2FtcF9uViwgaW50ZXJjZXB0ID0gcmVwKDEsIHNhbXBfblYpKQptZXNzYWdlKCdwc2V1ZG9kYXRhIHNhbXBsZWQ6ICcsIHN5c3RlbS50aW1lKApyZXN1bHRfc2FtcCA8LSBpbmxhKAogIGZvcm11bGEgPSBzYW1wX2Zvcm11bGEsCiAgZGF0YSA9IHNhbXBfZGF0YSwKICBmYW1pbHkgPSAncG9pc3NvbicsCiAgY29udHJvbC5wcmVkaWN0b3IgPSBsaXN0KEEgPSBzYW1wX09ic2VydmF0aW9uTWF0cml4KSwKICBFID0gc2FtcF9FX3BvaW50X3Byb2Nlc3MsCiAgdmVyYm9zZSA9IFRSVUUKKQopWydlbGFwc2VkJ10sICcgc2Vjb25kcycpCnJlc3VsdF9zYW1wJHN1bW1hcnkuZml4ZWQKcmVzdWx0X3NhbXAkc3VtbWFyeS5oeXBlcnBhcgoKIyBQbG90IHN1cmZhY2UuCnByb2pfbWFyZ2luX3NhbXAgPC0gaW5sYS5tZXNoLnByb2plY3RvcihtYXJnaW5fc2FtcCwgZGltcyA9IGMoTlBJWF9YLCBOUElYX1kpKQpwbG90KGltKHQoaW5sYS5tZXNoLnByb2plY3QocHJval9tYXJnaW5fc2FtcCwgcmVzdWx0X3NhbXAkc3VtbWFyeS5yYW5kb20kaWR4WywnbWVhbiddKSksCiAgICAgICAgeHJhbmdlID0gRnJhbWUoYmVpKSR4ICsgYygtTUFSR0lOLCBNQVJHSU4pLAogICAgICAgIHlyYW5nZSA9IEZyYW1lKGJlaSkkeSArIGMoLU1BUkdJTiwgTUFSR0lOKSwKICAgICAgICB1bml0bmFtZSA9IGMoJ21ldGVyJywgJ21ldGVycycpKSwKICAgICAgICBtYWluID0gJ1Bvc3RlcmlvciBNZWFuIExvZy1JbnRlbnNpdHknKQpwbG90KFdpbmRvdyhiZWlfc2FtcCksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWlfc2FtcCwgcGNoID0gJy4nLCBjb2wgPSAnd2hpdGUnKQpwbG90KGltKHQoaW5sYS5tZXNoLnByb2plY3QocHJval9tYXJnaW5fc2FtcCwgcmVzdWx0X3NhbXAkc3VtbWFyeS5yYW5kb20kaWR4Wywnc2QnXSkpLAogICAgICAgIHhyYW5nZSA9IEZyYW1lKGJlaSkkeCArIGMoLU1BUkdJTiwgTUFSR0lOKSwKICAgICAgICB5cmFuZ2UgPSBGcmFtZShiZWkpJHkgKyBjKC1NQVJHSU4sIE1BUkdJTiksCiAgICAgICAgdW5pdG5hbWUgPSBjKCdtZXRlcicsICdtZXRlcnMnKSksCiAgICAgICAgbWFpbiA9ICdQb3N0ZXJpb3IgU0QgTG9nLUludGVuc2l0eScpCnBsb3QoV2luZG93KGJlaV9zYW1wKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaV9zYW1wLCBwY2ggPSAnLicsIGNvbCA9ICd3aGl0ZScpCmBgYAoKCiMjIEJlaSBEYXRhc2V0IGFuZCBgaW5sYWJydWAKCmBgYHtyIGJlaWZ1bGxsZ2NwLCBjYWNoZSA9IFRSVUV9CmJlaV9mdWxsX3NwZGYgPC0gYXMuU3BhdGlhbFBvaW50cy5wcHAoYmVpKQpjbXBfZnVsbCA8LSBjb29yZGluYXRlcyB+IG15U21vb3RoKG1hcCA9IGNvb3JkaW5hdGVzLCBtb2RlbCA9IG1hcmdpbl9zcGRlKSArIEludGVyY2VwdAptZXNzYWdlKCdpbmxhYnJ1IGZ1bGw6ICcsIHN5c3RlbS50aW1lKApiZWlfZnVsbF9sZ2NwIDwtIGxnY3AoY21wX2Z1bGwsIGJlaV9mdWxsX3NwZGYsIG9wdGlvbnMgPSBsaXN0KHZlcmJvc2UgPSBUUlVFKSkKKVsnZWxhcHNlZCddLCAnIHNlY29uZHMnKQpiZWlfZnVsbF9sZ2NwJHN1bW1hcnkuZml4ZWQKYmVpX2Z1bGxfbGdjcCRzdW1tYXJ5Lmh5cGVycGFyCgojIFBsb3QgcG9zdGVyaW9yIG1lYW5zIGFuZCBwb3N0ZXJpb3Igc2QuCmxhbWJkYV9mdWxsIDwtIHByZWRpY3QoYmVpX2Z1bGxfbGdjcCwgcGl4ZWxzKGJlaV9mdWxsX21lc2gpLCB+IGV4cChteVNtb290aCArIEludGVyY2VwdCkpCnBsb3QobGFtYmRhX2Z1bGwpCnBsb3QoV2luZG93KGJlaSksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWksIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKcGxvdChsYW1iZGFfZnVsbFsnc2QnXSkKcGxvdChXaW5kb3coYmVpKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaSwgcGNoID0gJy4nLCBjb2wgPSAnd2hpdGUnKQpgYGAKCmBgYHtyIGJlaWhvbGVsZ2NwLCBjYWNoZSA9IFRSVUV9CmJlaV9ob2xlX3NwZGYgPC0gYXMuU3BhdGlhbFBvaW50cy5wcHAoYmVpX2hvbGUpCmNtcF9ob2xlIDwtIGNvb3JkaW5hdGVzIH4gbXlTbW9vdGgobWFwID0gY29vcmRpbmF0ZXMsIG1vZGVsID0gbWFyZ2luX2hvbGVfc3BkZSkgKyBJbnRlcmNlcHQKbWVzc2FnZSgnaW5sYWJydSB3aXRoIGhvbGVzOiAnLCBzeXN0ZW0udGltZSgKYmVpX2hvbGVfbGdjcCA8LSBsZ2NwKGNtcF9ob2xlLCBiZWlfaG9sZV9zcGRmLCBvcHRpb25zID0gbGlzdCh2ZXJib3NlID0gVFJVRSkpCilbJ2VsYXBzZWQnXSwgJyBzZWNvbmRzJykKYmVpX2hvbGVfbGdjcCRzdW1tYXJ5LmZpeGVkCmJlaV9ob2xlX2xnY3Akc3VtbWFyeS5oeXBlcnBhcgoKIyBQbG90IHBvc3RlcmlvciBtZWFucyBhbmQgcG9zdGVyaW9yIHNkLgpsYW1iZGFfaG9sZSA8LSBwcmVkaWN0KGJlaV9ob2xlX2xnY3AsIHBpeGVscyhiZWlfaG9sZV9tZXNoMCksIH4gZXhwKG15U21vb3RoICsgSW50ZXJjZXB0KSkKcGxvdChsYW1iZGFfaG9sZSkKcGxvdChXaW5kb3coYmVpX2hvbGUpLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpX2hvbGUsIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKcGxvdChsYW1iZGFfaG9sZVsnc2QnXSkKcGxvdChXaW5kb3coYmVpX2hvbGUpLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpX2hvbGUsIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKYGBgCgpgYGB7ciBiZWlzYW1wbGdjcCwgY2FjaGUgPSBUUlVFfQpiZWlfc2FtcF9zcGRmIDwtIGFzLlNwYXRpYWxQb2ludHMucHBwKGJlaV9zYW1wKQpjbXBfc2FtcCA8LSBjb29yZGluYXRlcyB+IG15U21vb3RoKG1hcCA9IGNvb3JkaW5hdGVzLCBtb2RlbCA9IG1hcmdpbl9zYW1wX3NwZGUpICsgSW50ZXJjZXB0Cm1lc3NhZ2UoJ2lubGFicnUgcXVhZHJhdHM6ICcsIHN5c3RlbS50aW1lKApiZWlfc2FtcF9sZ2NwIDwtIGxnY3AoY21wX3NhbXAsIGJlaV9zYW1wX3NwZGYsIG9wdGlvbnMgPSBsaXN0KHZlcmJvc2UgPSBUUlVFKSkKKVsnZWxhcHNlZCddLCAnIHNlY29uZHMnKQoKIyBQbG90IHBvc3RlcmlvciBtZWFucyBhbmQgcG9zdGVyaW9yIHNkLgpsYW1iZGFfc2FtcCA8LSBwcmVkaWN0KGJlaV9zYW1wX2xnY3AsIHBpeGVscyhiZWlfc2FtcF9tZXNoMCksIH4gZXhwKG15U21vb3RoICsgSW50ZXJjZXB0KSkKcGxvdChsYW1iZGFfc2FtcCkKcGxvdChXaW5kb3coYmVpKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcGxvdChXaW5kb3coYmVpX3NhbXApLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpX3NhbXAsIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKcGxvdChsYW1iZGFfc2FtcFsnc2QnXSkKcGxvdChXaW5kb3coYmVpKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcGxvdChXaW5kb3coYmVpX3NhbXApLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpX3NhbXAsIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKYGBgCgoKIyBSZWZlcmVuY2VzCgo=